प्रोग्रामर स्तर पर C ++ std :: atomic के साथ क्या गारंटी है?


9

मैंने कई लेख, वार्ता और स्टैकओवरफ़्लो प्रश्नों के बारे में सुना और पढ़ा है std::atomic, और मैं यह सुनिश्चित करना चाहूंगा कि मैंने इसे अच्छी तरह से समझा है। क्योंकि मैं अभी भी थोड़ा उलझन में हूं क्योंकि कैश लाइन MESI (या व्युत्पन्न) कैश सुसंगतता प्रोटोकॉल, स्टोर बफ़र्स, अमान्य कतारों, और इतने पर संभावित देरी के कारण दृश्यता लिखती है।

मैंने पढ़ा है कि x86 में एक मजबूत मेमोरी मॉडल है, और अगर कैश अमान्य होने में देरी हो रही है तो x86 परिचालन शुरू कर सकता है। लेकिन मुझे अब केवल उस चीज पर दिलचस्पी है जो मुझे एक सी ++ प्रोग्रामर के रूप में ग्रहण करना चाहिए, स्वतंत्र रूप से प्लेटफॉर्म के रूप में।

[T1: thread1 T2: thread2 V1: साझा परमाणु चर]

मैं समझता हूँ कि std :: परमाणु गारंटी देता है कि,

(1) कोई डेटा दौड़ चर पर नहीं होती (कैश लाइन के लिए विशेष पहुंच के लिए धन्यवाद)।

(२) जिस मेमोरी_ऑर्डर का हम उपयोग करते हैं, उसके आधार पर यह (बाधाओं के साथ) गारंटी देता है कि अनुक्रमिक स्थिरता (एक बाधा से पहले, एक बाधा या दोनों के बाद) होती है।

(3) T1 पर एक परमाणु लेखन (V1) के बाद, T2 पर एक परमाणु RMW (V1) सुसंगत होगा (इसकी कैश लाइन T1 पर लिखित मूल्य के साथ अद्यतन की गई होगी)।

लेकिन कैश सुसंगत प्राइमर के रूप में ,

इन सभी चीजों का निहितार्थ यह है कि, डिफ़ॉल्ट रूप से, लोड हो सकता है बासी डेटा (यदि एक अमान्य अनुरोध अनुरोध अमान्य कतार में बैठा था)

तो, निम्नलिखित सही है?

(4) इस std::atomicबात की गारंटी नहीं देता है कि T1 पर परमाणु लेखन (V) के बाद T2 किसी परमाणु रीड (V) पर 'बासी' मान नहीं पढ़ेगा।

यदि प्रश्न (4) सही है: यदि T1 पर एटॉमिक राइट कैश लाइन को विलंबित करता है, तो देरी क्यों नहीं होती है, T2 अमान्य आरएमडब्ल्यू ऑपरेशन के लिए प्रभावी होने का इंतजार क्यों कर रहा है, लेकिन परमाणु रीड पर नहीं?

प्रश्न अगर (4) गलत है: जब कोई थ्रेड 'बासी' मान पढ़ सकता है और निष्पादन में "यह दिखाई दे रहा है", तब?

मैं आपके उत्तरों की बहुत सराहना करता हूं

अपडेट १

तो ऐसा लगता है कि मैं (3) पर गलत था। प्रारंभिक V1 = 0 के लिए निम्नलिखित इंटरलेव की कल्पना करें:

T1: W(1)
T2:      R(0) M(++) W(1)

भले ही टी 2 का आरएमडब्ल्यू इस मामले में डब्ल्यू (1) के बाद पूरी तरह से होने की गारंटी है, फिर भी यह 'बासी' मूल्य (मैं गलत था) पढ़ सकता हूं। इसके अनुसार, परमाणु पूर्ण कैश सुसंगतता की गारंटी नहीं देता है, केवल अनुक्रमिक स्थिरता।

अपडेट २

(5) अब इस उदाहरण की कल्पना करें (x = y = 0 और परमाणु हैं):

T1: x = 1;
T2: y = 1;
T3: if (x==1 && y==0) print("msg");

हमने जो बात की है, उसके अनुसार स्क्रीन पर प्रदर्शित "संदेश" हमें टी 1 के बाद निष्पादित की गई टी 2 से आगे की जानकारी नहीं देगा। तो या तो निम्नलिखित निष्पादन हो सकते हैं:

  • टी 1 <टी 3 <टी 2
  • T1 <T2 <T3 (जहां T3 x = 1 को देखता है लेकिन y = 1 को अभी तक नहीं)

क्या वह सही है?

(६) यदि कोई धागा हमेशा st बासी ’मूल्यों को पढ़ सकता है, तो क्या होगा यदि हमने ठेठ" प्रकाशित "परिदृश्य लिया, लेकिन संकेत देने के बजाय कि कुछ डेटा तैयार है, हम सिर्फ विपरीत (डेटा हटाएं) करते हैं?

T1: delete gameObjectPtr; is_enabled.store(false, std::memory_order_release);
T2: while (is_enabled.load(std::memory_order_acquire)) gameObjectPtr->doSomething();

जहां T2 अभी भी हटाए गए ptr का उपयोग कर रहा है, जब तक कि is_enabled गलत नहीं है।

(() इसके अलावा, यह तथ्य कि थ्रेड्स 'बासी' मूल्यों को पढ़ सकते हैं, का अर्थ है कि एक म्यूटेक्स को केवल एक लॉक-फ्री परमाणु अधिकार के साथ लागू नहीं किया जा सकता है? इसे थ्रेड्स के बीच एक सिंक्र तंत्र की आवश्यकता होगी। क्या इसके लिए लॉकबल परमाणु की आवश्यकता होगी?

जवाबों:


3
  1. हां, डेटा रेस नहीं हैं
  2. हां, उचित memory_orderमूल्यों के साथ आप अनुक्रमिक स्थिरता की गारंटी दे सकते हैं
  3. परमाणु पठन-संशोधित-लेखन हमेशा पहले या पूरी तरह से एक परमाणु के एक ही चर के बाद लिखने से पहले होगा
  4. हां, T1 पर परमाणु लिखने के बाद T2 एक वैरिएबल से एक बासी मान पढ़ सकता है

परमाणु पढ़ने-संशोधित-लिखने के संचालन उनकी परमाणुता की गारंटी के लिए एक तरह से निर्दिष्ट हैं। यदि एक और धागा प्रारंभिक रीड के बाद और आरएमडब्ल्यू ऑपरेशन के लिखने से पहले मूल्य पर लिख सकता है, तो वह ऑपरेशन परमाणु नहीं होगा।

थ्रेड्स हमेशा बासी मूल्यों को पढ़ सकते हैं, सिवाय इसके कि जब-जब सापेक्ष ऑर्डर देने की गारंटी दी जाए ।

यदि कोई RMW ऑपरेशन "बासी" मान को पढ़ता है, तो यह गारंटी देता है कि यह जो लेखन उत्पन्न करता है, वह अन्य थ्रेड्स के किसी भी लिखने से पहले दिखाई देगा जो उसके द्वारा पढ़ा गया मान अधिलेखित कर देगा।

उदाहरण के लिए अपडेट करें

यदि T1 लिखता है x=1और T2 x++, xशुरुआत में 0 के साथ है , तो भंडारण के दृष्टिकोण से विकल्प xहैं:

  1. T1 का लेखन पहले है, इसलिए T1 लिखता है x=1, फिर T2 पढ़ता है x==1, वृद्धि करता है कि 2 और वापस लिखता है x=2एक एकल परमाणु संचालन के रूप में।

  2. T1 का लेखन दूसरा है। टी 2 पढ़ता है x==0, इसे 1 तक बढ़ाता है, और x=1एक ही ऑपरेशन के रूप में वापस लिखता है , फिर टी 1 लिखता है x=1

हालाँकि, बशर्ते इन दोनों थ्रेड्स के बीच सिंक्रोनाइज़ेशन के कोई अन्य बिंदु नहीं हैं, थ्रेड्स मेमोरी में फ़्लश नहीं किए गए ऑपरेशन के साथ आगे बढ़ सकते हैं।

इस प्रकार टी 1 जारी कर सकता है x=1, फिर अन्य चीजों के साथ आगे बढ़ें, भले ही टी 2 अभी भी पढ़ेगा x==0(और इस प्रकार लिखें x=1)।

यदि सिंक्रनाइज़ेशन के कोई अन्य बिंदु हैं, तो यह स्पष्ट हो जाएगा कि कौन सा धागा xपहले संशोधित किया गया है , क्योंकि उन सिंक्रनाइज़ेशन अंक एक आदेश को बाध्य करेंगे।

यह सबसे स्पष्ट है यदि आपके पास आरएमडब्ल्यू ऑपरेशन से पढ़े गए मूल्य पर एक सशर्त है।

अपडेट २

  1. यदि आप memory_order_seq_cstसभी परमाणु परिचालनों के लिए (डिफ़ॉल्ट) उपयोग करते हैं तो आपको इस प्रकार की चिंता करने की आवश्यकता नहीं है। कार्यक्रम के दृष्टिकोण से, यदि आप "संदेश" देखते हैं तो T1 भाग गया, फिर T3, फिर T2।

यदि आप अन्य मेमोरी ऑर्डरिंग (विशेष रूप से memory_order_relaxed) का उपयोग करते हैं तो आप अपने कोड में अन्य परिदृश्य देख सकते हैं।

  1. इस मामले में, आपके पास एक बग है। मान लीजिए कि is_enabledझंडा सच है, जब टी 2 अपने whileपाश में प्रवेश करता है, इसलिए यह शरीर को चलाने का फैसला करता है। T1 अब डेटा को हटाता है, और T2 तब पॉइंटर को हटाता है, जो कि एक लटकने वाला पॉइंटर है, और अपरिभाषित व्यवहार रहता है । ध्वज पर डेटा की दौड़ को रोकने से परे परमाणु किसी भी तरह से मदद या बाधा नहीं करते हैं।

  2. आप एक एकल परमाणु चर के साथ एक म्यूटेक्स को लागू कर सकते हैं


बहुत बहुत धन्यवाद @Anthony Wiliams आपके त्वरित उत्तर के लिए। मैंने अपने प्रश्न को 'बासी' मान पढ़ने वाले RMW के उदाहरण के साथ अद्यतन किया है। इस उदाहरण को देखते हुए, आप रिश्तेदार के आदेश का क्या मतलब रखते हैं और किसी भी लिखने से पहले टी 2 का डब्ल्यू (1) दिखाई देगा? क्या इसका मतलब यह है कि एक बार T2 ने T1 के बदलावों को देखा है और वह T2 के W (1) को नहीं पढ़ेगा?
अल्बर्ट कैलदास

इसलिए यदि "थ्रेड्स हमेशा बासी मूल्यों को पढ़ सकते हैं" तो इसका मतलब है कि कैश सुसंगतता की गारंटी नहीं है (कम से कम सी ++ प्रोग्रामर पर)। क्या आप मेरे अपडेट 2 पर एक नज़र डाल सकते हैं?
अल्बर्ट कैलदास

अब मैं देखता हूं कि मुझे पूरी तरह से यह समझने के लिए भाषा और हार्डवेयर मेमोरी मॉडल पर अधिक ध्यान देना चाहिए था, वह वह टुकड़ा था जो मुझे याद आ रहा था। आपका बहुत बहुत धन्यवाद!
अल्बर्ट कैलदास

1

(3) के बारे में - यह उपयोग किए गए मेमोरी ऑर्डर पर निर्भर करता है। यदि दोनों, स्टोर और आरएमडब्ल्यू ऑपरेशन का उपयोग करते हैं std::memory_order_seq_cst, तो दोनों ऑपरेशन किसी तरह से ऑर्डर किए जाते हैं - यानी, या तो स्टोर आरएमडब्ल्यू से पहले होता है, या दूसरे तरीके से गोल होता है। यदि स्टोर आरएमडब्ल्यू से पहले ऑर्डर करता है, तो यह गारंटी दी जाती है कि आरएमडब्ल्यू ऑपरेशन स्टोर किए गए मूल्य को "देखता है"। यदि स्टोर आरएमडब्ल्यू के बाद ऑर्डर किया जाता है, तो यह आरएमडब्ल्यू ऑपरेशन द्वारा लिखित मूल्य को अधिलेखित कर देगा।

यदि आप अधिक आराम से मेमोरी ऑर्डर का उपयोग करते हैं, तो संशोधनों को किसी तरह से आदेश दिया जाएगा (चर का संशोधन आदेश), लेकिन आपके पास इस बात की कोई गारंटी नहीं है कि आरएमडब्ल्यू स्टोर स्टोर से मूल्य "देखता है" - भले ही आरएमडब्ल्यू ऑपरेशन आदेश है के बाद चर के संशोधन के क्रम में लिखने।

यदि आप अभी तक एक और लेख पढ़ना चाहते हैं, तो मैं आपको C / C ++ प्रोग्रामर के लिए मेमोरी मॉडल का उल्लेख कर सकता हूं ।


लेख के लिए धन्यवाद, मैंने अभी तक इसे नहीं पढ़ा था। यहां तक ​​कि अगर यह काफी पुराना है, तो मेरे विचारों को एक साथ रखना उपयोगी है।
अल्बर्ट कैलदास

1
यह सुनकर खुशी हुई - यह लेख मेरे गुरु की थीसिस से थोड़ा विस्तारित और संशोधित अध्याय है। :-) यह मेमोरी मॉडल पर ध्यान केंद्रित करता है जैसा कि C ++ 11 को पेश किया गया है; मैं इसे C ++ 14/17 में पेश किए गए (छोटे) परिवर्तनों को प्रतिबिंबित करने के लिए अपडेट कर सकता हूं। कृपया मुझे बताएं कि क्या आपके पास सुधार के लिए कोई टिप्पणी या सुझाव है!
16
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.