2011-11-28 19 views
24

मैं वैश्विक चर का उपयोग कर इंटर-थ्रेड संचार लागू कर रहा हूं।असाइनमेंट ऑपरेटर '=' परमाणु है?

//global var 
volatile bool is_true = true; 

//thread 1 
void thread_1() 
{ 
    while(1){ 
     int rint = rand() % 10; 
     if(is_true) { 
      cout << "thread_1: "<< rint <<endl; //thread_1 prints some stuff 
      if(rint == 3) 
       is_true = false; //here, tells thread_2 to start printing stuff 
     } 
    } 
} 

//thread 2 
void thread_2() 
{ 
    while(1){ 
     int rint = rand() % 10; 
     if(! is_true) { //if is_true == false 
      cout << "thread_1: "<< rint <<endl; //thread_2 prints some stuff 
      if(rint == 7) //7 
       is_true = true; //here, tells thread_1 to start printing stuff 
     } 
    } 
} 

int main() 
{ 
    HANDLE t1 = CreateThread(0,0, thread_1, 0,0,0); 
    HANDLE t2 = CreateThread(0,0, thread_2, 0,0,0); 
    Sleep(9999999); 
    return 0; 
} 

प्रश्न

उपरोक्त कोड में, मैं एक वैश्विक वर volatile bool is_true का उपयोग thread_1 और thread_2 के बीच स्विच करने के लिए मुद्रण।

मुझे आश्चर्य है क्या यह असाइनमेंट ऑपरेशन का उपयोग करने के लिए थ्रेड-सुरक्षित है या नहीं?

+0

मैं एक परमाणु विनिमय आदिम उपयोग करने के लिए पसंद करते हैं, लेकिन मैं एक परिदृश्य है, जिसमें आप एक समस्या मिल चाहता हूँ ... –

+0

@KerrekSB, इस परिदृश्य बाहर काम नहीं कर सकते? खैर, मैंने इसे अपने प्रश्न का प्रदर्शन करने के लिए अभी सुधार किया है :) :) – Alcott

+0

ठीक है, मेरा मतलब है लोड और स्टोर्स का एक अनुक्रम जो दोनों धागे को महत्वपूर्ण खंड में प्रवेश करने के लिए पर्याप्त रूप से टूटा जाएगा ... आमतौर पर ऐसा अनुक्रम प्रदर्शित करने में सक्षम होना चाहिए यह दिखाने के लिए कि क्यों कुछ कोड सही नहीं है। हालांकि, मैं इसे यहां नहीं देख सकता। मुझे अभी भी कोड पसंद नहीं है, लेकिन मैं साबित नहीं कर सकता कि क्यों। –

उत्तर

54

यह कोड Win32 पर थ्रेड-सुरक्षित होने की गारंटी नहीं है, क्योंकि Win32 केवल अत्याधुनिक 4-बाइट और पॉइंटर आकार के मानों के लिए परमाणुता की गारंटी देता है। bool उन प्रकारों में से एक होने की गारंटी नहीं है।

मान लीजिए कि bool एक 1-बाइट प्रकार है:

जो लोग यह कैसे विफल हो सकता है की एक वास्तविक उदाहरण की मांग के लिए (यह आमतौर पर एक 1-बाइट प्रकार है।)। मान लीजिए कि आपके is_true वैरिएबल को bool वैरिएबल के समीप संग्रहीत किया जाता है (चलो इसे other_bool पर कॉल करें), ताकि वे दोनों एक ही 4-बाइट लाइन साझा कर सकें। कंक्रीटेंस के लिए, मान लीजिए कि is_true पता 0x1000 और other_bool पते 0x1001 पर है। मान लीजिए कि दोनों मान प्रारंभ में false हैं, और एक थ्रेड is_true को एक ही समय में अपडेट करने का निर्णय लेता है, एक और थ्रेड other_bool को अपडेट करने का प्रयास करता है। आपरेशन के निम्न क्रम हो सकता है:

  • थ्रेड 1 4 बाइट is_true और other_bool युक्त मूल्य लोड करके true को is_true स्थापित करने के लिए तैयार करता है। थ्रेड 1 0x00000000 पढ़ता है।
  • थ्रेड 2 से true को is_true और other_bool युक्त 4-बाइट मान लोड करके सेट करने के लिए तैयार करता है। थ्रेड 2 0x00000000 पढ़ता है।
  • थ्रेड 1 0x30000001 उत्पादित is_true से संबंधित 4-बाइट मान में बाइट को अद्यतन करता है।
  • थ्रेड 2 other_bool करने के लिए इसी, 0x00000100 उत्पादन 4 बाइट मूल्य में बाइट अद्यतन करता है।
  • थ्रेड 1 अद्यतन मूल्य को स्मृति में संग्रहीत करता है। is_true अब true और other_bool अब false है।
  • थ्रेड 2 अद्यतन मूल्य को स्मृति में संग्रहीत करता है। is_true अब false और other_bool अब true है।

गौर करें कि अंत में इस क्रम, is_true करने के लिए अद्यतन खो गया था, क्योंकि यह धागा 2, जो is_true के एक पुराने मूल्य पर कब्जा कर लिया द्वारा ओवरराइट किया गया था।

यह इतना है कि 86 इस प्रकार की त्रुटि के बहुत क्षमाशील, क्योंकि यह बाइट-बारीक अद्यतन का समर्थन करता है और एक बहुत तंग स्मृति मॉडल है। अन्य Win32 प्रोसेसर क्षमा नहीं कर रहे हैं। आरआईएससी चिप्स, उदाहरण के लिए, अक्सर बाइट-ग्रैनुलर अपडेट का समर्थन नहीं करते हैं, और यदि वे करते हैं, तो वे आमतौर पर बहुत कमजोर मेमोरी मॉडल होते हैं।

+0

संरेखण के बारे में महान स्पष्टीकरण। आपका उदाहरण स्पष्ट रूप से दिखाता है कि असाइनमेंट परमाणु नहीं है। –

+3

बीटीडब्लू, 'अस्थिर' कीवर्ड बल को एक आर्किटेयर को लक्षित आर्किटेक्चर के लिए थ्रेड-सुरक्षित तरीके से संरेखित करने के लिए मजबूर नहीं करेगा (यानी स्टोर करने के लिए 32-बिट x86 प्रोसेसर के लिए 4-बाइट सेल में 'बूल' मान)? इससे समस्या हल हो जाएगी। क्या कुछ कंपाइलर्स इस चाल को करते हैं? –

+1

@PavelGatilov नवीनतम मसौदे मानक के अनुसार, संकलक को इस व्यवहार को रोकना चाहिए – curiousguy

7

नहीं, इसकी नहीं ..... आपको किसी प्रकार के लॉकिंग आदिम का उपयोग करने की आवश्यकता है। प्लेटफार्म के आधार पर, आप या तो बूस्ट वाले का उपयोग कर सकते हैं, या यदि मूल विंडोज़ जा रहे हैं, तो इंटरलाक्ड कॉम्पैयर एक्सचेंज जैसे कुछ।

वास्तव में आपकी स्थिति में आप कुछ धागे सुरक्षित घटना तंत्रियों का उपयोग कर सकते हैं ताकि आप जो भी चाहते हैं उसे शुरू करने के लिए अपने अन्य धागे को 'सिग्नल' कर सकें।

+2

ओपी के कोड के साथ * गलत * क्या है, यद्यपि? क्या आप एक परिदृश्य तैयार कर सकते हैं जहां कुछ टूट जाता है? –

+0

@ केरेकस्क, मुझे क्षमा करें, ओपी? वो क्या है? – Alcott

+2

@ एल्कोट: "ओपी" का मतलब है "मूल पोस्टर", जैसा कि सवाल पूछता है। इस मामले में, यह आप है। :-) – ruakh

-1

कोड के इस टुकड़े की धागा सुरक्षितता असाइनमेंट की परमाणुता पर निर्भर नहीं है। दोनों धागे दिनचर्या बदले में सख्ती से काम करते हैं। कोई दौड़ की स्थिति नहीं है: थ्रेड_1 कुछ यादृच्छिक संख्या प्राप्त करने तक आउटपुट सामग्री करेगा जिसके बाद यह 'आउटपुट सेक्शन' छोड़ देगा और अन्य धागे को इसमें काम करने दें। ध्यान देने योग्य बातें की एक जोड़ी रहे हैं, हालांकि:

  • रैंड() फ़ंक्शन थ्रेड-सुरक्षित नहीं किया जा सकता (नहीं है, हालांकि यहां दिए गए कोड में समस्या)
  • आप उपयोग नहीं करना चाहिए Win32 समारोह CreateThread() , विशेष रूप से जब आप सीआरटी लिबरली फ़ंक्शंस का उपयोग कर रहे हैं जो (संभावित रूप से) वैश्विक चर का उपयोग करते हैं। इसके बजाय _beginthreadex() का प्रयोग करें।
+2

" _Both थ्रेड रूटीन बारी में सख्ती से काम करते हैं। _ "गले लगाओ – curiousguy

4

सभी आधुनिक प्रोसेसर पर, आप मान सकते हैं कि पढ़ता है और स्वाभाविक रूप से गठबंधन देशी प्रकार के लेखन परमाणु कर रहे हैं। जब तक मेमोरी बस कम से कम चौड़ी होती है जैसे कि टाइप पढ़ा या लिखा जा रहा है, तो सीपीयू इन प्रकारों को एक बस बस लेनदेन में पढ़ता है और लिखता है, जिससे अन्य थ्रेडों को आधे पूर्ण राज्य में देखना असंभव हो जाता है। X86 और x64 पर, कोई गारंटी नहीं है कि आठ बाइट्स से बड़े परमाणुओं को पढ़ता है और लिखता है। इसका मतलब है कि स्ट्रीमिंग सिम एक्सटेंशन (एसएसई) रजिस्ट्रार, और स्ट्रिंग ऑपरेशंस के 16-बाइट पढ़ते हैं और लिखते हैं, शायद परमाणु नहीं हो सकते हैं।

पढ़ता है और जो प्राकृतिक रूप से गठबंधन के लिए नहीं कर रहे हैं उदाहरण के प्रकार, DWORDs है कि चार-बाइट पार लिखने के लिखते हैं सीमाओं-कर रहे हैं परमाणु होने की गारंटी नहीं। सीपीयू को इन्हें कई बस लेनदेन के रूप में पढ़ना और लिखना पड़ सकता है, जो किसी अन्य थ्रेड को पढ़ने या लिखने के बीच में डेटा को संशोधित या देखने की अनुमति दे सकता है।

संबंधित मुद्दे