2015-05-27 11 views
5

मुझे इस बारे में अनिश्चितता है कि सी ++ 11 में परमाणु चर के मेमोरी ऑर्डरिंग की गारंटी कैसे अन्य मेमोरी पर परिचालन को प्रभावित करती है।सी ++ 11 परमाणु मेमोरी ऑर्डर गैर-परमाणु चर के साथ

मान लें कि मेरे पास एक थ्रेड है जो आवधिक रूप से एक मूल्य को अद्यतन करने के लिए लेखन फ़ंक्शन को कॉल करता है, और दूसरा धागा जो वर्तमान मान प्राप्त करने के लिए पढ़ता है। क्या यह गारंटी है कि d = value; के प्रभाव a = version; के प्रभाव से पहले नहीं देखा जाएगा, और b = version; के प्रभाव से पहले देखा जाएगा?

atomic<int> a {0}; 
atomic<int> b {0}; 
double d; 

void write(int version, double value) { 
    a = version; 
    d = value; 
    b = version; 
} 

double read() { 
    int x,y; 
    double ret; 
    do { 
     x = b; 
     ret = d; 
     y = a; 
    } while (x != y); 
    return ret; 
} 
+0

आपके कोड में कोई बाधा नहीं है; आपने इसे "स्मृति-बाधाओं" के साथ क्यों टैग किया? –

+1

क्योंकि मैंने सोचा था कि उत्तर कुछ ऐसा हो सकता है "यह सही नहीं है, आपको स्मृति-बाधा का उपयोग करने की आवश्यकता है"। –

+1

आप जो भी पूछ रहे हैं उसे पुनः लिखना चाहेंगे। इस सवाल का जवाब देने वाले लोग घृणित रूप से पैडेंटिक हैं, और इस बारे में उत्सुक लोगों के लिए अच्छे से अधिक नुकसान पहुंचा रहे हैं। –

उत्तर

1

यह गारंटी है कि d = value; के प्रभाव a = version; के प्रभाव से पहले नहीं देखा जाएगा, और b = version; के प्रभाव से पहले देखा जाएगा है?

हाँ, यह है। ऐसा इसलिए है क्योंकि अनुक्रमिक स्थिरता बाधाatomic<> चर पढ़ने या लिखने पर निहित है।

इसके बजाय मूल्य के संशोधन से पहले और इसके बाद दो परमाणु चर में version टैग भंडारण की, इससे पहले कि आप और संशोधन के बाद एकल परमाणु चर को बढ़ा सकते हैं:

atomic<int> a = {0}; 
double d; 

void write(double value) 
{ 
    a = a + 1; // 'a' become odd 
    d = value; //or other modification of protected value(s) 
    a = a + 1; // 'a' become even, but not equal to the one before modification 
} 

double read(void) 
{ 
    int x; 
    double ret; 
    do 
    { 
     x = a; 
     ret = value; // or other action with protected value(s) 
    } while((x & 2) || (x != a)); 
    return ret; 
} 

यह seqlock में के रूप में जाना जाता है लिनक्स कर्नेल: http://en.wikipedia.org/wiki/Seqlock

2

आपका वस्तु d लिखा गया है और दो धागे से पढ़ सकते हैं और यह परमाणु नहीं है है। के रूप में बहु सूत्रण पर सी ++ मानक में सुझाव यह असुरक्षित है:

1,10/4 दो अभिव्यक्ति मूल्यांकन संघर्ष करता है, तो उनमें से एक एक स्मृति स्थान को संशोधित करता है और अन्य एक तक पहुंचता है या एक ही स्मृति स्थान संशोधित करता है। यदि यह न तो अलग धागे में दो परस्पर विरोधी कार्रवाई, जो परमाणु नहीं है कम से कम एक में शामिल है, और अन्य से पहले होता है

1,10/21 एक कार्यक्रम के निष्पादन के एक डेटा दौड़ में शामिल है। ऐसे किसी भी डेटा रेस के परिणाम अपरिभाषित व्यवहार में परिणाम।

महत्वपूर्ण संपादित करें:

अपने गैर परमाणु मामले में, आप पढ़ने और लिखने के बीच आदेश के बारे में कोई गारंटी नहीं है। आपको यह भी गारंटी नहीं है कि पाठक लेखक द्वारा लिखे गए एक मूल्य को पढ़ेगा (यह short article explains the risk गैर-परमाणु चर के लिए)।

फिर भी, अपने पाठक का पाश आसपास के परमाणु चर, जिसके लिए वहाँ मजबूत की गारंटी देता है की एक परीक्षण के आधार पर खत्म। यह मानते हुए कि version कभी नहीं लेखक अलग कॉल के बीच दोहराता है, और उलटे क्रम है जिसमें आप उनके मूल्य प्राप्त दिया:

  • d पढ़ने d लिखने की तुलना के आदेश दुर्भाग्यपूर्ण अगर दो एटोमिक्स बराबर हैं नहीं किया जा सकता ।
  • इसी प्रकार, यदि दो परमाणु बराबर हैं तो पढ़ा गया मूल्य असंगत नहीं हो सकता है।

इसका मतलब है कि अपने गैर परमाणु, पाश करने के लिए धन्यवाद पर एक प्रतिकूल रेस स्थिति के मामले में, यदि आप अंतिम-अप करेंगे पिछले value पढ़ने।

+0

शुक्र है, मानक मानक रूप से "असुरक्षित" शब्द का उपयोग नहीं करता है। ओपी के कार्यक्रम का वर्णन करने का एक बेहतर तरीका "गलत" या "टूटा हुआ" होगा। –

+0

सिर्फ इसलिए कि मूल्यांकन "संघर्ष" का मतलब यह नहीं है कि यह डेटा रेस (और इसलिए यूबी) है। आपको एक बेहतर उद्धरण की आवश्यकता है। –

+0

@KerrekSB मेरी संदिग्ध शैली के लिए खेद है। मैं हाइलाइट करना चाहता था कि यह असुरक्षित है, और मानक का संदर्भ लें। मैंने इसे "सुझाया" में बदल दिया। क्या यह टूटा हुआ की बजाय अनिर्दिष्ट या अनिर्धारित नहीं होगा? – Christophe

3

नियम यह है कि, एक write धागा है कि एक बार कार्यान्वित करता है, और उस को संशोधित करता a, b या d और कुछ नहीं दिया,

  • आप किसी भी समय एक अलग धागे से a और b पढ़ सकते हैं, और
  • अगर आप b पढ़ सकते हैं और version में संग्रहीत देखते हैं, तो
    • आप d पढ़ सकते हैं; और
    • जो आप पढ़ते हैं वह value होगा।

ध्यान दें कि कि क्या दूसरे भाग सच है स्मृति आदेश पर निर्भर करता है; यह डिफ़ॉल्ट एक (memory_order_seq_cst) के साथ सच है।

+0

यदि पाठक ए और बी के लिए एक ही संस्करण देखता है, तो क्या मुझे उस संस्करण के साथ सेट किए गए मान को पढ़ने की गारंटी नहीं है? मेरी समझ से, मेरे पास यह गारंटी है जब तक कि लिखने के लिए कॉल में 'a = version' से पहले दिखाई देने के लिए लाइन' d = value' के प्रभावों के लिए संभव नहीं है। –

+0

@ जेफरीडाल्ला टाज़ज़ा यहां बिंदु यह है कि आप 'डी' नहीं पढ़ सकते हैं जबतक कि आप नहीं जानते कि लेखन समाप्त हो गया है, और आप 'बी' पढ़कर ऐसा करते हैं और देख रहे हैं कि' संस्करण' इसमें संग्रहीत किया गया है। आपका कोड बिना शर्त के 'डी 'से पढ़ता है, जो डेटा रेस और इसलिए अपरिभाषित व्यवहार का कारण बनता है। –

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