2010-01-04 18 views
22

तो मैं स्मृति मॉडल है कि आगामी C++ 0x मानक का हिस्सा है के बारे में पढ़ रहा था। हालांकि, मैं संकलक को करने की अनुमति देने के लिए कुछ प्रतिबंधों के बारे में थोड़ा उलझन में हूं, खासकर सट्टा भार और दुकानों के बारे में।C++ 0x स्मृति मॉडल और सट्टा लोड/स्टोर

शुरू में, प्रासंगिक सामान में से कुछ:

Hans Boehm's pages about threads and the memory model in C++0x

Boehm, "Threads Cannot be Implemented as a Library"

Boehm and Adve, "Foundations of the C++ Concurrency Memory Model"

Sutter, "Prism: A Principle-Based Sequential Memory Model for Microsoft Native Code Platforms", N2197

Boehm, "Concurrency memory model compiler consequences", N2338

अब, मूल विचार अनिवार्य रूप से "डेटा-रेस-नि: शुल्क कार्यक्रम के लिए अनुक्रमिक संगति" है, जो प्रोग्रामिंग की आसानी और संकलक और हार्डवेयर के अवसरों का अनुकूलन करने के लिए अनुमति देता है के बीच एक सभ्य समझौता हो रहा है है। एक डाटा दौड़ स्मृति स्थान के लिए उनमें से कम से कम एक भंडार होने के लिये करता है, तो अलग धागे से एक ही स्मृति स्थान के लिए दो पहुंच आदेश दिया नहीं कर रहे हैं, परिभाषित किया गया है, और उनमें से कम से कम एक एक तुल्यकालन कार्रवाई नहीं है। यह संकेत मिलता है कि साझा किए गए डेटा के लिए सभी पढ़ने/लिखने के लिए एक्सेस इस तरह के परमाणु चर पर mutexes या संचालन (अच्छी तरह से, यह विशेषज्ञों केवल के लिए आदेश देने आराम स्मृति परमाणु चर पर संचालित करने के लिए संभव है, लेकिन के रूप में कुछ तुल्यकालन तंत्र के माध्यम से होना चाहिए, डिफ़ॉल्ट अनुक्रमिक स्थिरता के लिए प्रदान करता है)।

इस के प्रकाश में, मैं साधारण साझा चर पर नकली या सट्टा लोड/स्टोर के बारे में प्रतिबंध के बारे में संदेह में हूँ। उदाहरण के लिए, N2338 में हम उदाहरण है

switch (y) { 
    case 0: x = 17; w = 1; break; 
    case 1: x = 17; w = 3; break; 
    case 2: w = 9; break; 
    case 3: x = 17; w = 1; break; 
    case 4: x = 17; w = 3; break; 
    case 5: x = 17; w = 9; break; 
    default: x = 17; w = 42; break; 
} 

y == 2 एक्स के लिए एक नकली लिखने जो एक समस्या हो सकती है वहाँ है अगर के बाद से जो संकलक

tmp = x; x = 17; 
switch (y) { 
    case 0: w = 1; break; 
    case 1: w = 3; break; 
    case 2: x = tmp; w = 9; break; 
    case 3: w = 1; break; 
    case 4: w = 3; break; 
    case 5: w = 9; break; 
    default: w = 42; break; 
} 

में रूपांतरित करने के लिए अनुमति नहीं है यदि एक और धागा समवर्ती रूप से एक्स अद्यतन कर रहे थे। लेकिन, यह एक समस्या क्यों है? यह एक डेटा रेस है, जिसे वैसे भी प्रतिबंधित है; इस मामले में, कंपाइलर सिर्फ दो बार x लिखकर इसे और भी खराब बनाता है, लेकिन डेटा लिखने के लिए भी एक ही लेखन पर्याप्त होगा, नहीं? अर्थात। एक उचित सी ++ 0x प्रोग्राम को एक्स तक पहुंच सिंक्रनाइज़ करने की आवश्यकता होगी, इस मामले में अब डेटा रेस नहीं होगी, और नकली स्टोर कोई समस्या नहीं होगी?

मैं भी इसी तरह N2197 में उदाहरण 3.1.3 और अन्य उदाहरण में से कुछ लेकर संदेह में हूँ, लेकिन शायद ऊपर जारी करने के लिए एक स्पष्टीकरण वह भी समझा जाएगा।

संपादित करें: उत्तर:

कारण है कि सट्टा भंडार एक समस्या हैं कि ऊपर स्विच बयान के उदाहरण में, प्रोग्रामर सशर्त ताला ही अगर y एक्स की रक्षा के अधिग्रहण के लिए निर्वाचित हो सकता है = 2! इसलिए सट्टा स्टोर एक डेटा रेस पेश कर सकता है जो मूल कोड में नहीं था, और इस प्रकार परिवर्तन को प्रतिबंधित किया गया है। एक ही तर्क N2197 में उदाहरण 3.1.3 पर भी लागू होता है।

+0

शायद http://groups.google.com/group/comp.std.c++ –

उत्तर

7

मैं आपके द्वारा संदर्भित सभी सामग्री से परिचित नहीं हूं, लेकिन ध्यान दें कि y == 2 मामले में, कोड के पहले बिट में, x बिल्कुल लिखा नहीं गया है (या उस मामले के लिए पढ़ें) । कोड के दूसरे बिट में, यह दो बार लिखा जाता है। बनाम लिखने के बजाय यह दो बार लिखने से अधिक अंतर है (कम से कम, यह मौजूदा थ्रेडिंग मॉडल जैसे कि पर्थ्रेड) में है।साथ ही, एक ऐसा मूल्य संग्रहीत करना जो अन्यथा संग्रहीत नहीं किया जाता है, एक बार बनाम बनाकर एक बार बनाकर एक अंतर होता है। इन दोनों कारणों से, आप नहीं चाहते हैं कि कंपाइलर्स सिर्फ tmp = x; x = 17; x = tmp; के साथ नो-ऑप को प्रतिस्थापित करें।

मान लीजिए कि थ्रेड ए यह मानना ​​चाहता है कि कोई अन्य थ्रेड x को संशोधित नहीं करता है। यह उचित है कि यह उम्मीद की जा सके कि यदि y 2 है, और यह x के लिए मान लिखता है, तो उसे वापस पढ़ता है, यह उस मूल्य को वापस ले लेगा जो उसने लिखा है। लेकिन अगर थ्रेड बी आपके कोड के दूसरे बिट को निष्पादित कर रहा है, तो थ्रेड ए एक्स को लिख सकता है और बाद में इसे पढ़ सकता है, और मूल मान वापस ले सकता है, क्योंकि थ्रेड बी ने लिखने से पहले "पहले" सहेजा और इसे "बहाल" किया। या यह 17 वापस आ सकता है, क्योंकि थ्रेड बी ने लिखने के बाद 17 "संग्रहित किया है, और थ्रेड ए के बाद फिर से" tmp "संग्रहीत किया है। थ्रेड ए जो भी सिंक्रनाइज़ेशन पसंद करता है वह कर सकता है, और इससे मदद नहीं मिलेगी, क्योंकि थ्रेड बी सिंक्रनाइज़ नहीं है। कारण यह सिंक्रनाइज़ नहीं है (वाई == 2 मामले में) यह है कि यह एक्स का उपयोग नहीं कर रहा है। तो इस बात की अवधारणा कि "एक्स का उपयोग करता है" कोड का एक विशेष बिट थ्रेडिंग मॉडल के लिए महत्वपूर्ण है, जिसका मतलब है कि संकलक को कोड को बदलने के लिए कोड को बदलने की अनुमति नहीं दी जा सकती है जब इसे "नहीं" होना चाहिए।

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

इसलिए, हालांकि मैं "डेटा रेस" की सी ++ 0x की परिभाषा से परिचित नहीं हूं, मुझे लगता है कि इसमें कुछ स्थितियां शामिल हैं जहां प्रोग्रामर को यह मानने की अनुमति है कि कोई ऑब्जेक्ट नहीं लिखा गया है, और यह परिवर्तन होगा उन शर्तों का उल्लंघन करें। मैं अनुमान लगाता हूं कि यदि y == 2, तो आपका मूल कोड, समवर्ती कोड के साथ: x = 42; x = 1; z = x किसी अन्य थ्रेड में, डेटा रेस के रूप में परिभाषित नहीं किया गया है। या कम से कम अगर यह एक डेटा दौड़ है, यह नहीं है जो मूल्य या तो 17 के साथ समाप्त करने के लिए जेड परमिट, या 42 है

पर विचार करें कि इस कार्यक्रम में, वाई में मान 2 इंगित करने के लिए इस्तेमाल किया जा सकता है, "वहाँ अन्य थ्रेड चल रहे हैं: एक्स को संशोधित न करें, क्योंकि हम यहां सिंक्रनाइज़ नहीं हैं, इसलिए डेटा रेस पेश करेंगे "। शायद कारण यह नहीं है कि कोई सिंक्रनाइज़ेशन नहीं है, यह है कि वाई के अन्य सभी मामलों में, x तक पहुंच के साथ कोई अन्य थ्रेड नहीं चल रहा है। यह मेरे लिए उचित लगता है कि C++ 0x इस तरह कोड का समर्थन करना चाहते हैं:

if (single_threaded) { 
    x = 17; 
} else { 
    sendMessageThatSafelySetsXTo(17); 
} 

जाहिर है तो, आप नहीं चाहते कि बदल रहे हैं:

tmp = x; 
x = 17; 
if (!single_threaded) { 
    x = tmp; 
    sendMessageThatSafelySetsXTo(17); 
} 

मूलतः एक ही परिवर्तन है कौन सा जैसा कि आपके उदाहरण में है, लेकिन केवल 2 मामलों के साथ, यह एक अच्छा कोड-आकार अनुकूलन की तरह दिखने के लिए पर्याप्त है।

+0

सी ++ 0x में, डेटा रेस तब होती है जब अलग-अलग धागे द्वारा समान स्मृति स्थान पर दो पहुंच का आदेश नहीं दिया जाता है उनमें से कम से कम एक स्मृति स्थान पर संग्रहीत करता है, और उनमें से कम से कम एक सिंक्रनाइज़ेशन कार्रवाई नहीं है। मैंने इसे प्रश्न में जोड़ा। – janneb

+0

+1 उत्कृष्ट उत्तर – swegi

+0

@janneb।धन्यवाद, उस स्थिति में पहले कोड स्निपेट में कोई डेटा रेस नहीं है यदि y == 2, और दूसरा थ्रेड x तक पहुंचता है, लेकिन दूसरे स्निपेट में डेटा रेस होती है यदि y == 2 और दूसरा थ्रेड x तक पहुंचता है। स्पष्ट रूप से संकलक को अन्यथा रेस-फ्री कोड में डेटा दौड़ जोड़ने की अनुमति नहीं दी जानी चाहिए, या पूरा मॉडल बेकार है। इसलिए परिवर्तन प्रतिबंधित है। –

4

यदि y==2, और दूसरा धागा x को संशोधित या पढ़ता है, तो मूल नमूने में दौड़ की स्थिति कैसी है? यह धागा कभी भी x को छूता नहीं है, इसलिए अन्य धागे इतनी आसानी से ऐसा कर सकते हैं।

लेकिन पुनर्नवीनीकरण संस्करण के साथ, हमारा धागा x को संशोधित करता है, अगर केवल अस्थायी रूप से, तो यदि कोई अन्य थ्रेड भी इसका उपयोग करता है, तो अब हमारे पास दौड़ की स्थिति है जहां कोई भी मौजूद नहीं था।

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