क्या C ++ वाष्पशील कीवर्ड मेमोरी बाड़ का परिचय देता है?


85

मैं समझता हूँ कि volatile संकलक को सूचित करता है कि मान को बदला जा सकता है, लेकिन इस कार्यक्षमता को पूरा करने के लिए, संकलक को काम करने के लिए मेमोरी बाड़ लगाने की आवश्यकता है?

मेरी समझ से, अस्थिर वस्तुओं पर परिचालन के अनुक्रम को फिर से व्यवस्थित नहीं किया जा सकता है और इसे संरक्षित किया जाना चाहिए। ऐसा लगता है कि कुछ स्मृति बाड़ आवश्यक हैं और यह वास्तव में इस के आसपास एक रास्ता नहीं है। क्या मैं यह कहने में सही हूं?


पर एक दिलचस्प चर्चा है इस संबंधित प्रश्न

जोनाथन वैखली लिखते हैं :

... अलग-अलग वाष्पशील चर तक पहुंच को कंपाइलर द्वारा तब तक पुन: व्यवस्थित नहीं किया जा सकता है जब तक वे अलग-अलग पूर्ण अभिव्यक्तियों में होते हैं ... सही है कि वाष्पशील-सुरक्षा के लिए अस्थिर बेकार है, लेकिन उन कारणों के लिए नहीं जो वह देता है। ऐसा नहीं है क्योंकि संकलक अस्थिर वस्तुओं तक पहुंच को फिर से शुरू कर सकता है, लेकिन क्योंकि सीपीयू उन्हें फिर से व्यवस्थित कर सकता है। परमाणु संचालन और मेमोरी बाधा कंपाइलर और सीपीयू को फिर से चालू होने से रोकते हैं

जो करने के लिए दाऊद Schwartz उत्तर टिप्पणी में :

... कोई अंतर नहीं है, C ++ मानक के दृष्टिकोण से, कंपाइलर कुछ कर रहा है और कंपाइलर निर्देश जो कि हार्डवेयर को कुछ करने का कारण बनता है। यदि सीपीयू वाष्पशील तक पहुंच को पुन: व्यवस्थित कर सकता है, तो मानक को इसकी आवश्यकता नहीं है कि उनके आदेश को संरक्षित किया जाए। ...

... C ++ मानक पुनरावर्तन के बारे में कोई भेद नहीं करता है। और आप यह तर्क नहीं दे सकते कि सीपीयू उन्हें बिना किसी अवलोकनीय प्रभाव के साथ फिर से व्यवस्थित कर सकता है ताकि ठीक हो - सी ++ मानक उनके आदेश को अवलोकनीय के रूप में परिभाषित करता है। एक प्लेटफ़ॉर्म पर C ++ मानक के साथ एक कंपाइलर का अनुपालन होता है यदि यह कोड उत्पन्न करता है जो प्लेटफ़ॉर्म को वह बनाता है जो मानक की आवश्यकता होती है। यदि मानक को वाष्पशील तक पहुंच की आवश्यकता होती है, तो उसे पुन: व्यवस्थित नहीं किया जाता है, तो एक प्लेटफ़ॉर्म जो उनके अनुरूप नहीं है। ...

मेरा कहना यह है कि यदि C ++ मानक कंपाइलर को अलग-अलग वाष्पशील तक पहुंच को पुन: व्यवस्थित करने से प्रतिबंधित करता है, इस सिद्धांत पर कि इस तरह के एक्सेस का क्रम प्रोग्राम के अवलोकनीय व्यवहार का हिस्सा है, तो इसके लिए कंपाइलर को कोड का अनुकरण करने की आवश्यकता होती है जो CPU को करने से रोकता है। इसलिए। कंपाइलर क्या करता है और कंपाइलर का जेनरेट कोड सीपीयू क्या करता है, इसके बीच का मानक अंतर नहीं करता है।

जिससे दो प्रश्न निकलते हैं: क्या उनमें से कोई "सही" है? वास्तविक कार्यान्वयन वास्तव में क्या करते हैं?


9
इसका ज्यादातर अर्थ यह है कि संकलक को उस चर को रजिस्टर में नहीं रखना चाहिए। स्रोत कोड में प्रत्येक असाइनमेंट और रीडिंग को बाइनरी कोड में मेमोरी एक्सेस के अनुरूप होना चाहिए।
बेसिल स्टारीनेवविच


1
मुझे संदेह है कि कोई भी मेमोरी बाड़ अप्रभावी होगी यदि मूल्य एक आंतरिक रजिस्टर में संग्रहीत किया जाना था। मुझे लगता है कि आपको अभी भी समवर्ती स्थिति में अन्य सुरक्षात्मक उपाय करने की आवश्यकता है।
गेलिक

जहां तक ​​मुझे पता है, अस्थिर का उपयोग चर के लिए किया जाता है जिसे हार्डवेयर द्वारा बदला जा सकता है (अक्सर माइक्रोकंट्रोलर के साथ उपयोग किया जाता है)। इसका सीधा सा मतलब है कि चर को पढ़ना एक अलग क्रम में नहीं किया जा सकता है और इसे अनुकूलित नहीं किया जा सकता है। हालांकि यह C है, लेकिन ++ में समान होना चाहिए।
मस्तूल

1
@ मैं अभी तक एक कंपाइलर को देख रहा हूं जो कि volatileचर के रीड को सीपीयू कैश द्वारा अनुकूलित किए जाने से रोकता है । या तो ये सभी संकलक गैर-अनुरूप हैं या मानक का मतलब यह नहीं है कि आप क्या सोचते हैं इसका मतलब है। (मानक संकलक क्या करता है और संकलक सीपीयू क्या करता है, के बीच अंतर नहीं करता है। यह कोड संकलित करने का काम है जो, जब, मानक का अनुपालन करता है।)
डेविड श्वार्ट्ज

जवाबों:


58

यह समझाने के बजाय कि क्या करना volatileहै, मुझे समझाने की अनुमति दें कि आपको कब उपयोग करना चाहिए volatile

  • जब एक सिग्नल हैंडलर के अंदर। क्योंकि एक volatileवैरिएबल पर लिखना बहुत ही एकमात्र चीज है जो मानक आपको सिग्नल हैंडलर के भीतर से करने की अनुमति देता है। C ++ 11 के बाद से आप उपयोग कर सकते हैंstd::atomic उस उद्देश्य के लिए , लेकिन केवल अगर परमाणु लॉक-फ़्री है।
  • जब इंटेल के अनुसार काम करते हैंsetjmp
  • सीधे हार्डवेयर के साथ काम करते समय और आप यह सुनिश्चित करना चाहते हैं कि कंपाइलर आपके रीड को ऑप्टिमाइज़ नहीं करता है या लिखता है।

उदाहरण के लिए:

volatile int *foo = some_memory_mapped_device;
while (*foo)
    ; // wait until *foo turns false

के बिना volatileविनिर्देशक के , कंपाइलर को लूप को पूरी तरह से अनुकूलित करने की अनुमति है। volatileविनिर्देशक संकलक यह कल्पना नहीं कर सकते हैं कि 2 बाद में पढ़ता है कि समान मान बताता है।

ध्यान दें कि volatile धागे से कोई लेना-देना नहीं है। उपरोक्त उदाहरण काम नहीं करता है अगर वहाँ एक अलग धागा लेखन था*foo क्योंकि इसमें कोई अधिग्रहण ऑपरेशन शामिल नहीं है।

अन्य सभी मामलों में, उपयोग को volatileगैर-पोर्टेबल माना जाना चाहिए और प्री-सी ++ 11 कंपाइलर और कंपाइलर एक्सटेंशन (जैसे कि एमएससीएसवाई /volatile:msस्विच, जो कि X86 / I64 के तहत डिफ़ॉल्ट रूप से सक्षम है) के साथ काम करने के अलावा कोड की समीक्षा नहीं करनी चाहिए।


5
यह सख्त है "यह मानकर नहीं चल सकता है कि 2 बाद के रीड्स समान मान लौटाते हैं"। यहां तक ​​कि अगर आप केवल एक बार पढ़ते हैं और / या मूल्य को फेंक देते हैं, तो पढ़ा जाना चाहिए।
दीक्षा

1
सिग्नल हैंडलर में उपयोग और setjmpमानक हैं दो गारंटी देता है। दूसरी ओर, कम से कम शुरुआत में, इरादा मेमोरी मैप्ड IO का समर्थन करना था। कुछ प्रोसेसर पर बाड़ या मेम्बर की आवश्यकता हो सकती है।
जेम्स कंज

@philipxy को छोड़कर किसी को नहीं पता कि "रीड" का क्या मतलब है। उदाहरण के लिए, किसी का मानना ​​है कि स्मृति से एक वास्तविक पढ़ा जाना चाहिए - कोई संकलक नहीं जो मुझे volatileएक्सेस पर सीपीयू कैश को बायपास करने की कोशिशों के बारे में पता है ।
डेविड श्वार्ट्ज

@JamesKanze: ऐसा नहीं है। पुन: सिग्नल हैंडलर मानक कहते हैं कि सिग्नल हैंडलिंग के दौरान केवल अस्थिर एसटीडी :: sig_atomic_t और लॉक-फ्री परमाणु वस्तुओं ने मूल्यों को परिभाषित किया है। लेकिन यह भी कहता है कि अस्थिर वस्तुओं तक पहुंच अवलोकनीय है।
फिलीपिस्क

1
@DavidSchwartz कुछ संकलक-आर्किटेक्चर जोड़े उन प्रभावों को प्राप्त करने के लिए वास्तविक प्रभावों और कामकाजी कार्यक्रमों तक पहुंच के मानक-निर्दिष्ट अनुक्रम को मैप करते हैं। तथ्य यह है कि ऐसी कुछ जोड़ियों में कोई मानचित्रण नहीं है या एक तुच्छ अनहेल्फ़ मैपिंग कार्यान्वयन की गुणवत्ता के लिए प्रासंगिक है, लेकिन हाथ की बात नहीं है।
फिलीपिक्सी

25

क्या C ++ वाष्पशील कीवर्ड मेमोरी बाड़ का परिचय देता है?

सी ++ संकलक जो विनिर्देश के अनुरूप है, उसे मेमोरी बाड़ लगाने की आवश्यकता नहीं है। आपका विशेष संकलक हो सकता है; अपने संकलक के लेखकों को अपना प्रश्न निर्देशित करें।

C ++ में "अस्थिर" के फ़ंक्शन का थ्रेडिंग से कोई लेना-देना नहीं है। याद रखें, "अस्थिर" का उद्देश्य कंपाइलर ऑप्टिमाइज़ेशन को अक्षम करना है ताकि एक रजिस्टर से पढ़ना जो बाहरी स्थितियों के कारण बदल रहा है वह अनुकूलित नहीं है। एक मेमोरी एड्रेस जो एक अलग सीपीयू पर एक अलग सीपीयू द्वारा एक रजिस्टर में लिखा जा रहा है जो कि बहिर्जात स्थितियों के कारण बदल रहा है? फिर से, यदि कुछ संकलक लेखकों ने विभिन्न सीपीयू पर विभिन्न थ्रेड्स द्वारा लिखे जा रहे मेमोरी एड्रेस का इलाज करने के लिए चुना है, जैसे कि वे बाहरी स्थितियों के कारण बदलते हुए रजिस्टर थे, तो यह उनका व्यवसाय है; उन्हें ऐसा करने की आवश्यकता नहीं है। न ही इनकी आवश्यकता होती है - भले ही यह मेमोरी बाड़ को पेश करता हो - उदाहरण के लिए, सुनिश्चित करें कि प्रत्येक थ्रेड एक सुसंगत देखता है वाष्पशील का क्रम पढ़ता और लिखता है।

वास्तव में, अस्थिरता सी / सी ++ में फैलने के लिए बहुत अधिक बेकार है। इससे बचने के लिए सबसे अच्छा अभ्यास है।

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

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


19
"वाष्पशील C / C ++ में बहुत बेकार है।" हर्गिज नहीं! आपके पास दुनिया का एक बहुत ही usermode-desktop-centric view है ... लेकिन ज्यादातर C और C ++ कोड एम्बेडेड सिस्टम पर चलते हैं, जहां मेमोरी-मैप्ड I / O के लिए वाष्पशील की बहुत आवश्यकता होती है।
बेन वोइगट

12
और इसका कारण यह है कि वाष्पशील पहुंच संरक्षित नहीं है, क्योंकि बहिर्जात की स्थिति स्मृति स्थानों को बदल सकती है। बहुत पहुँच ही आगे की क्रियाओं को गति प्रदान कर सकती है। उदाहरण के लिए, एक फीफो को आगे बढ़ाना, या एक बाधा ध्वज को साफ करना बहुत आम है।
बेन वोइग्ट

3
@BenVoigt: प्रभावी रूप से थ्रेडिंग विकारों से निपटने के लिए बेकार मेरा इच्छित अर्थ था।
एरिक लिपर्ट

4
@DavidSchwartz मानक स्पष्ट रूप से यह गारंटी नहीं दे सकता है कि स्मृति आईओ कैसे काम करती है। लेकिन मेमोरी मैप्ड IO क्यों volatileC मानक में पेश किया गया था। फिर भी, क्योंकि मानक उन चीजों को निर्दिष्ट नहीं कर सकता है, जो वास्तव में "पहुंच" पर होती हैं, यह कहता है कि "किसी वस्तु की पहुंच किस प्रकार होती है जिसमें अस्थिर-योग्य प्रकार कार्यान्वयन-परिभाषित है।" आज तक बहुत सारे कार्यान्वयन आज भी एक उपयोगी परिभाषा प्रदान नहीं करते हैं, जो IMHO मानक की भावना का उल्लंघन करता है, भले ही वह पत्र के अनुरूप हो।
जेम्स कांज़

8
वह संपादन एक निश्चित सुधार है, लेकिन आपका स्पष्टीकरण अभी भी "स्मृति को बाहरी रूप से बदला जा सकता है" पर केंद्रित है। volatileशब्दार्थ इससे अधिक मजबूत होते हैं, संकलक को प्रत्येक अनुरोधित पहुंच (1.9 / 8, 1.9 / 12) उत्पन्न करनी होती है, केवल इस बात की गारंटी नहीं होती है कि अंतर्जात परिवर्तन का पता लगाया जाता है (1.10 / 27)। मेमोरी-मैपेड I / O की दुनिया में, एक मेमोरी रीड में एक संपत्ति पाने वाले की तरह मनमाने ढंग से जुड़े तर्क हो सकते हैं। आप अपने द्वारा बताए गए नियमों के अनुसार संपत्ति पाने वालों को कॉल का अनुकूलन नहीं करेंगे volatile, न ही मानक इसकी अनुमति देता है।
बेन वोइग्ट

13

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

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


5
अच्छी तरह से समझाया, धन्यवाद। जब तक कार्यक्रम का कोई अपरिभाषित व्यवहार नहीं होता है तब तक मानक केवल वाष्पशील तक पहुंच के अनुक्रम को परिभाषित करता है
जोनाथन वेकली

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

आपको क्यों लगता है कि मैं उसकी अनदेखी कर रहा हूं? मेरे तर्क का कौन सा हिस्सा आपको लगता है कि अमान्य है? मैं 100% सहमत हूं कि संकलक पूरी तरह से किसी भी तुल्यकालन से गुजरने के अधिकार में है।
डेविड श्वार्ट्ज

2
यह केवल गलत है, या कम से कम, यह आवश्यक को अनदेखा करता है। volatileधागे से कोई लेना-देना नहीं है; यह मूल उद्देश्य स्मृति मैप किए गए IO का समर्थन करना था। और कम से कम कुछ प्रोसेसर पर, मेमोरी मैप्ड IO का समर्थन करने के लिए बाड़ की आवश्यकता होगी। (संकलक ऐसा नहीं करते हैं, लेकिन यह एक अलग मुद्दा है।)
जेम्स कांज़

@JamesKanze volatileका थ्रेड्स के साथ बहुत कुछ है: volatileमेमोरी के साथ डील जो कि संकलक के बिना एक्सेस की जा सकती है यह जानते हुए कि इसे एक्सेस किया जा सकता है, और यह विशिष्ट सीपीयू पर थ्रेड्स के बीच साझा डेटा के कई वास्तविक विश्व उपयोगों को कवर करता है।
जिज्ञासु १५'१

12

सबसे पहले, C ++ मानकों को पढ़ने / लिखने के लिए आवश्यक गैर-परमाणु हैं जो ठीक से ऑर्डर करने के लिए आवश्यक मेमोरी बैरियर की गारंटी नहीं देते हैं। एमएमआईओ, सिग्नल हैंडलिंग आदि के साथ उपयोग करने के लिए वाष्पशील चरों की सिफारिश की जाती है। अधिकांश कार्यान्वयनों में बहु-थ्रेडिंग के लिए वाष्पशील उपयोगी नहीं होता है और यह आमतौर पर अनुशंसित नहीं होता है।

अस्थिर पहुंच के कार्यान्वयन के संबंध में, यह संकलक विकल्प है।

इस लेख में , जीसीसी व्यवहार का वर्णन करते हुए दिखाया गया है कि आप वाष्पशील मेमोरी को लिखने के अनुक्रम का आदेश देने के लिए मेमोरी अवरोधक के रूप में एक वाष्पशील वस्तु का उपयोग नहीं कर सकते हैं।

आईसीसी व्यवहार के बारे में मुझे यह स्रोत यह बताते हुए भी मिला कि वाष्पशील मेमोरी एक्सेस को ऑर्डर करने की गारंटी नहीं देता है।

Microsoft VS2013 संकलक का एक अलग व्यवहार है। यह दस्तावेज़ीकरण बताता है कि कैसे वाष्पशील रिलीज़ / अधिग्रहण को लागू करता है और बहु-थ्रेडेड अनुप्रयोगों पर अस्थिर वस्तुओं को ताले / रिलीज़ में उपयोग करने में सक्षम बनाता है।

एक और पहलू जिस पर विचार करने की आवश्यकता है, वह यह है कि एक ही संकलक का व्यवहार भिन्न हो सकता है लक्षित हार्डवेयर वास्तुकला के आधार पर अस्थिरता के लिए । MSVS 2013 संकलक के बारे में यह पोस्ट स्पष्ट रूप से एआरएम प्लेटफार्मों के लिए अस्थिरता के साथ संकलन करने की बारीकियों को बताता है।

तो मेरा जवाब है:

क्या C ++ वाष्पशील कीवर्ड मेमोरी बाड़ का परिचय देता है?

होगा: गारंटी नहीं, शायद नहीं, लेकिन कुछ संकलक ऐसा कर सकते हैं। आपको इस तथ्य पर भरोसा नहीं करना चाहिए कि यह करता है।


2
यह अनुकूलन को रोकता नहीं है, यह सिर्फ कंपाइलर को कुछ बाधाओं से परे लोड और स्टोर को बदलने से रोकता है।
डिट्रीच एप्प

यह स्पष्ट नहीं है कि आप क्या कह रहे हैं। क्या आप कह रहे हैं कि ऐसा होता है कि कुछ अनिर्दिष्ट कंपाइलरों पर ऐसा होता है volatileजो कंपाइलर को लोड / स्टोर को पुनः व्यवस्थित करने से रोकता है? या आप कह रहे हैं कि C ++ मानक को ऐसा करने की आवश्यकता है? और अगर बाद में, क्या आप मूल प्रश्न में उद्धृत विपरीत के लिए मेरे तर्क का जवाब दे सकते हैं?
डेविड श्वार्ट्ज

@DavidSchwartz मानक लैवल्यू के माध्यम से एक्सेस के किसी भी रीक्रिएटिंग (किसी भी स्रोत से) को रोकता है volatile। चूंकि यह कार्यान्वयन तक "पहुंच" की परिभाषा को छोड़ देता है, हालांकि, यह हमें बहुत खरीद नहीं करता है अगर कार्यान्वयन परवाह नहीं करता है।
जेम्स कांजे

मुझे लगता है कि MSC संकलक के कुछ संस्करणों ने बाड़ शब्दार्थ को लागू किया volatile, लेकिन विजुअल स्टूडियो 2012 में संकलक से उत्पन्न कोड में कोई बाड़ नहीं है।
जेम्स कांज़

@JamesKanze मूल रूप से इसका मतलब है कि केवल पोर्टेबल व्यवहार volatileयह है कि विशेष रूप से मानक द्वारा गणना की गई है। ( setjmp, संकेत, और इतने पर।)
डेविड श्वार्ट्ज

7

कंपाइलर केवल इटेनियम आर्किटेक्चर पर एक मेमोरी फेंस सम्मिलित करता है, जहाँ तक मुझे पता है।

volatileकीवर्ड वास्तव में सबसे अच्छा अतुल्यकालिक परिवर्तन, जैसे, संकेत संचालकों और स्मृति-मैप किया रजिस्टरों के लिए प्रयोग किया जाता है; यह आमतौर पर मल्टीथ्रेडेड प्रोग्रामिंग के लिए उपयोग करने के लिए गलत उपकरण है।


1
की तरह। 'कंपाइलर' (msvc) एआरएम के अलावा अन्य आर्किटेक्चर और / वाष्पशील: ms स्विच का उपयोग (डिफ़ॉल्ट) होने पर एक मेमोरी फेंस सम्मिलित करता है। Msdn.microsoft.com/en-us/library/12a04hfd.aspx देखें । अन्य संकलक मेरी जानकारी के लिए अस्थिर चर पर बाड़ नहीं डालते हैं। जब तक हार्डवेयर, सिग्नल हैंडलर या नॉन-सी ++ 11 कंफर्मिंग कंपाइलर के साथ सीधे व्यवहार न किया जाए, तब तक वाष्पशील के उपयोग से बचा जाना चाहिए।
स्टीफन

@Stefan No. volatileकई उपयोगों के लिए अत्यंत उपयोगी है जो कभी हार्डवेयर के साथ सौदा नहीं करते हैं। जब भी आप C / C ++ कोड को अनुसरण करने वाले CPU कोड को जनरेट करना चाहते हैं, का उपयोग करें volatile
जिज्ञासु

7

यह निर्भर करता है कि कौन सा कंपाइलर "कंपाइलर" है। दृश्य C ++ करता है, 2005 के बाद से। लेकिन मानक को इसकी आवश्यकता नहीं है, इसलिए कुछ अन्य संकलक नहीं करते हैं।


VC ++ 2012 एक बाड़ डालने के लिए प्रतीत नहीं होता है: int volatile i; int main() { return i; }वास्तव में दो निर्देशों के साथ एक मुख्य उत्पन्न करता है mov eax, i; ret 0;:।
जेम्स कांज़

@JamesKanze: कौन सा संस्करण, बिल्कुल? और क्या आप किसी भी गैर-डिफ़ॉल्ट संकलन विकल्पों का उपयोग कर रहे हैं? मैं प्रलेखन (पहले प्रभावित संस्करण) और (नवीनतम संस्करण) पर भरोसा कर रहा हूं , जो निश्चित रूप से अधिग्रहण और रिलीज शब्दार्थ का उल्लेख करता है।
बेन वोइग्ट

cl /helpसंस्करण 18.00.21005.1 कहता है। निर्देशिका यह में है C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC। कमांड विंडो पर हैडर वीएस 2013 कहता है। इसलिए संस्करण के संबंध में ... मेरे द्वारा उपयोग किए गए एकमात्र विकल्प थे /c /O2 /Fa। (इसके बिना /O2, यह स्थानीय स्टैक फ्रेम को भी स्थापित करता है। लेकिन अभी भी कोई बाड़ निर्देश नहीं है।)
जेम्स कान्जे

@JamesKanze: मुझे आर्किटेक्चर में अधिक रुचि थी, उदाहरण के लिए "Microsoft (R) C / C ++ ऑप्टिमाइज़िंग कंपाइलर संस्करण 18.00.30723 x64 के लिए" शायद कोई बाड़ नहीं है क्योंकि x86 और x64 के पास अपने मेमोरी मॉडल में काफी मजबूत कैश कोअहर्ता की गारंटी के साथ शुरू करना है। ?
बेन वोइग्ट

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

5

यह बड़े पैमाने पर मेमोरी से है, और थ्रेड्स के बिना प्री-सी ++ 11 पर आधारित है। लेकिन कमेटी में थ्रेडिंग पर चर्चा में भाग लेने के बाद, मैं कह सकता हूं कि उस समिति द्वारा कभी कोई इरादा नहीं था volatileजो थ्रेड्स के बीच सिंक्रनाइज़ेशन के लिए इस्तेमाल किया जा सकता है। Microsoft ने इसे प्रस्तावित किया था, लेकिन प्रस्ताव आगे नहीं बढ़ा।

इसकी मुख्य विशिष्टता volatileयह है कि अस्थिरता का उपयोग एक "अवलोकन योग्य व्यवहार" का प्रतिनिधित्व करता है, जैसे कि IO। उसी तरह कंपाइलर विशिष्ट IO को पुन: व्यवस्थित या हटा नहीं सकता है, यह किसी अस्थिर ऑब्जेक्ट तक पहुँच को पुनः व्यवस्थित या हटा नहीं सकता है (या अधिक सही तरीके से, वाष्पशील अभिव्यक्ति के साथ एक वाष्पशील योग्य प्रकार के माध्यम से एक्सेस करता है)। वाष्पशील का मूल इरादा वास्तव में, स्मृति मैप किए गए IO का समर्थन करना था। हालांकि, इसके साथ "समस्या" यह है कि यह कार्यान्वयन है जो परिभाषित करता है कि "अस्थिर पहुंच" क्या है। और कई संकलक इसे लागू करते हैं जैसे कि परिभाषा "एक निर्देश है जो स्मृति को पढ़ता या लिखता है उसे निष्पादित किया गया है"। यदि यह एक कानूनी कानूनी है, भले ही बेकार परिभाषा, अगर कार्यान्वयन इसे निर्दिष्ट करता है। (मैं अभी तक किसी भी संकलक के लिए वास्तविक विनिर्देशन नहीं खोज पाया हूँ

तर्कपूर्वक (और यह एक तर्क है जिसे मैं स्वीकार करता हूं), यह मानक के इरादे का उल्लंघन करता है, क्योंकि जब तक कि हार्डवेयर पतों को मेमोरी मैप्ड आईओ के रूप में नहीं पहचानता है, और किसी भी पुन: व्यवस्थित करने आदि को रोकता है, आप मेमोरी मैप किए गए ओओ के लिए वाष्पशील का उपयोग भी नहीं कर सकते। कम से कम स्पार्क या इंटेल आर्किटेक्चर पर। कभी भी कम नहीं, उनमें से कोई भी कॉमरेड जो मैंने (सन सीसी, जी ++ और एमएससी) में देखा है, किसी भी बाड़ या यादगार निर्देशों का उत्पादन करते हैं। (उस समय के बारे में जब Microsoft ने नियमों का विस्तार करने का प्रस्ताव रखा था volatile, मुझे लगता है कि उनके कुछ कंपाइलरों ने उनके प्रस्ताव को लागू किया था, और वाष्पशील पहुंच के लिए बाड़ के निर्देशों का उत्सर्जन किया था। मैंने यह सत्यापित नहीं किया है कि हाल ही में कंपाइलर क्या करते हैं, लेकिन यह मुझे आश्चर्यचकित नहीं करेगा अगर यह निर्भर करता है। कुछ संकलक विकल्प पर। संस्करण मैं जाँचता हूं - मुझे लगता है कि यह VS6.0 था - हालांकि, बाड़ का उत्सर्जन नहीं किया।)


आप यह क्यों कहते हैं कि संकलक अस्थिर वस्तुओं तक पहुंच नहीं सकता है या पहुंच को हटा नहीं सकता है? निश्चित रूप से अगर पहुंच अवलोकनीय व्यवहार है, तो निश्चित रूप से सीपीयू को रोकना ठीक है, पोस्टिंग बफ़र्स, मेमोरी कंट्रोलर और बाकी सभी चीज़ों को भी रीक्रिएट करने से उतना ही महत्वपूर्ण है।
डेविड श्वार्ट्ज

@DavidSchwartz क्योंकि यही मानक कहता है। निश्चित रूप से, व्यावहारिक दृष्टिकोण से, मैंने जो कंपाइलर्स सत्यापित किया है वह पूरी तरह से बेकार है, लेकिन मानक नेवला-शब्द इतना पर्याप्त है कि वे अभी भी अनुरूपता का दावा कर सकते हैं (या यदि वे वास्तव में इसका दस्तावेजीकरण कर सकते हैं)।
जेम्स कंज

1
@DavidSchwartz: एक्सक्लूसिव (या mutex'd) मेमोरी-मैप्ड I / O के लिए बाह्य उपकरणों, volatileशब्दार्थ पूरी तरह से पर्याप्त हैं। आम तौर पर इस तरह के परिधीय अपने मेमोरी क्षेत्रों को गैर-परिवर्तनीय के रूप में रिपोर्ट करते हैं, जो हार्डवेयर स्तर पर पुन: व्यवस्थित करने में मदद करता है।
बेन वोइग्ट

@BenVoigt मैं किसी भी तरह से इसके बारे में सोच रहा था: यह विचार कि प्रोसेसर किसी तरह "जानता है" कि जिस पते के साथ वह काम कर रहा है वह मेमोरी मैप्ड IO है। जहां तक ​​मुझे पता है, स्पार्क्स के पास इसके लिए कोई समर्थन नहीं है, इसलिए यह अभी भी मेमोरी मैप किए गए IO के लिए एक स्पार्कc पर Sun CC और g ++ बना देगा। (जब मैंने इस पर ध्यान दिया, तो मुझे मुख्य रूप से एक स्पार्क में दिलचस्पी थी।)
जेम्स कान्ज

@JamesKanze: मैंने जो कुछ बहुत कम खोज किया, उससे ऐसा लगता है कि स्पार्क ने स्मृति के "वैकल्पिक विचारों" के लिए समर्पित पता सीमाएं हैं जो कि गैर-परिवर्तनीय हैं। जब तक ASI_REAL_IOपता स्थान के हिस्से में आपका अस्थिर पहुंच बिंदु है, मुझे लगता है कि आपको ठीक होना चाहिए। (एलटर एनआईओएस एमएमयू बाईपास को नियंत्रित करने वाले पते के उच्च बिट्स के साथ एक समान तकनीक का उपयोग करता है; मुझे यकीन है कि अन्य भी हैं)
बेन वायगेट

5

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

मेमोरी बाधाओं के बारे में थोड़ा सा स्पष्टीकरण। एक विशिष्ट CPU में मेमोरी एक्सेस के कई स्तर होते हैं। एक मेमोरी पाइपलाइन, कैश का कई स्तर, फिर रैम आदि है।

मेम्बर निर्देश पाइपलाइन को फ्लश करते हैं। वे उस क्रम को नहीं बदलते हैं जिसमें लिखा और लिखा जाता है, उसे निष्पादित किया जाता है, यह केवल बकाया लोगों को एक निश्चित समय पर निष्पादित करने के लिए मजबूर करता है। यह मल्टीथ्रेडेड कार्यक्रमों के लिए उपयोगी है, लेकिन बहुत अन्यथा नहीं।

Cache (s) सामान्यतः CPU के बीच स्वचालित रूप से सुसंगत होते हैं। यदि कोई सुनिश्चित करना चाहता है कि कैश रैम के साथ सिंक है, तो कैश फ्लश की जरूरत है। यह एक मेम्बर से बहुत अलग है।


1
तो आप कह रहे हैं कि C ++ मानक कहता है कि volatileबस संकलक अनुकूलन को निष्क्रिय करता है? इसका कोई मतलब नहीं है। संकलक किसी भी अनुकूलन कर सकता है, कम से कम सिद्धांत रूप में, सीपीयू द्वारा समान रूप से अच्छी तरह से किया जा सकता है। तो अगर मानक ने कहा कि यह सिर्फ संकलक अनुकूलन को अक्षम करता है, तो इसका मतलब है कि यह कोई व्यवहार प्रदान नहीं करेगा जो पोर्टेबल कोड पर बिल्कुल भी भरोसा नहीं कर सकता है। लेकिन यह स्पष्ट रूप से सच नहीं है क्योंकि पोर्टेबल कोड सम्मान setjmpऔर संकेतों के साथ अपने व्यवहार पर भरोसा कर सकता है।
डेविड शवार्ट्ज

1
@DavidSchwartz नहीं, मानक कहते हैं कि ऐसी कोई बात नहीं है। अनुकूलन को अक्षम करना वही है जो आमतौर पर मानक को लागू करने के लिए किया जाता है। मानक के लिए आवश्यक है कि सारगर्भित मशीन द्वारा आवश्यक रूप से अवलोकन व्यवहार उसी क्रम में हो। जब अमूर्त मशीन को किसी आदेश की आवश्यकता नहीं होती है, तो कार्यान्वयन किसी भी आदेश या किसी भी क्रम का उपयोग करने के लिए स्वतंत्र है। जब तक अतिरिक्त सिंक्रनाइज़ेशन लागू नहीं किया जाता है, विभिन्न थ्रेड्स में अस्थिर चर तक पहुंच का आदेश नहीं दिया जाता है।
एन। 'सर्वनाम' मी।

1
@DavidSchwartz मैं अभेद्य शब्दांकन के लिए माफी माँगता हूँ। मानक को आवश्यकता नहीं है कि अनुकूलन अक्षम हो। इसमें अनुकूलन की कोई धारणा नहीं है। बल्कि, यह व्यवहार को निर्दिष्ट करता है कि व्यवहार में कुछ ऑप्टिमाइज़ेशन को इस तरह से अक्षम करने के लिए कंपाइलरों की आवश्यकता होती है, ताकि पढ़े और लिखे जाने का अनुक्रम मानक के अनुरूप हो।
एन। 'सर्वनाम' मी।

1
सिवाय इसके कि इसकी आवश्यकता नहीं है, क्योंकि मानक "पढ़ने और लिखने के अवलोकन योग्य अनुक्रम" को परिभाषित करने के लिए कार्यान्वयन की अनुमति देता है, हालांकि वे चाहते हैं। यदि कार्यान्वयन अवलोकन योग्य अनुक्रमों को परिभाषित करने के लिए चुनते हैं, तो अनुकूलन को अक्षम करना पड़ता है, फिर वे करते हैं। यदि नहीं, तो नहीं। आपको पढ़ने और लिखने का एक पूर्वानुमेय अनुक्रम मिलता है, और यदि केवल, तो कार्यान्वयन ने आपको इसे देने के लिए चुना है।
डेविड श्वार्ट्ज

1
नहीं, कार्यान्वयन को परिभाषित करने की आवश्यकता है कि एक एकल पहुंच क्या है। इस तरह के एक्सेस का क्रम सार मशीन द्वारा निर्धारित किया गया है। एक कार्यान्वयन को आदेश को संरक्षित करना है। मानक स्पष्ट रूप से कहता है कि "अस्थिरता वस्तु को शामिल करने के लिए आक्रामक अनुकूलन से बचने के लिए कार्यान्वयन के लिए एक संकेत है", भले ही एक गैर-मानक भाग में हो, लेकिन इरादा स्पष्ट है।
एन। 'सर्वनाम' मी।

4

कंपाइलर को volatileएक्सेस के आसपास मेमोरी बाड़ लगाने की आवश्यकता होती है , और केवल अगर, volatileमानक कार्य में निर्दिष्ट के लिए उपयोग करने के लिए आवश्यक है (setjmp , सिग्नल हैंडलर, और इसी तरह) उस विशेष प्लेटफॉर्म पर।

ध्यान दें कि कुछ कंपाइलर volatileउन प्लेटफार्मों पर अधिक शक्तिशाली या उपयोगी बनाने के लिए C ++ मानक द्वारा आवश्यक होने से परे रास्ता बनाते हैं । पोर्टेबल कोड volatileC ++ मानक में निर्दिष्ट से परे कुछ भी करने के लिए भरोसा नहीं करना चाहिए ।


2

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

मैं रैम के साथ-साथ मेमोरी-मैप किए गए IO के लिए भी ऐसा करता हूं।

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

जैसा कि हम "सिस्टम" में अधिक सामान जमा करते हैं जो ऑब्जेक्ट कोड को निष्पादित करता है लगभग सभी दांव बंद हैं, कम से कम यह है कि मैं इस चर्चा को कैसे पढ़ता हूं। एक कंपाइलर कभी भी सभी ठिकानों को कैसे कवर कर सकता है?


0

मुझे लगता है कि सीपीयू क्या करते हैं की 2 धारणाओं से अस्थिर और निर्देश के पुन: प्रसारण के आस-पास भ्रम होता है:

  1. आउट-ऑफ-ऑर्डर निष्पादन।
  2. अन्य सीपीयू द्वारा देखी गई मेमोरी रीड / राइट की अनुक्रम (इस अर्थ में पुन: व्यवस्थित करना कि प्रत्येक सीपीयू एक अलग अनुक्रम देख सकता है)।

वाष्पशील प्रभावित करता है कि कैसे एक संकलक एकल थ्रेडेड निष्पादन मान कोड उत्पन्न करता है (इसमें व्यवधान भी शामिल है)। यह मेमोरी अवरोध निर्देशों के बारे में कुछ भी नहीं बताता है, लेकिन यह मेमोरी एक्सेस से संबंधित कुछ प्रकार के अनुकूलन करने से एक कंपाइलर को रोकता है।
एक रजिस्टर में एक कैश्ड का उपयोग करने के बजाय, एक विशिष्ट उदाहरण मेमोरी से एक मूल्य को फिर से प्राप्त करना है।

आउट-ऑफ-ऑर्डर निष्पादन

सीपीयू आउट-ऑफ-ऑर्डर / सट्टेबाजी से निर्देश निष्पादित कर सकता है बशर्ते कि अंतिम परिणाम मूल कोड में हो सकता है। सीपीयू उन परिवर्तनों का प्रदर्शन कर सकते हैं जो संकलक में अस्वीकृत हैं क्योंकि संकलक केवल उन परिवर्तनों को कर सकते हैं जो सभी परिस्थितियों में सही हैं। इसके विपरीत, सीपीयू इन ऑप्टिमाइज़ेशन की वैधता की जांच कर सकते हैं और गलत होने पर उन्हें वापस भेज सकते हैं।

अन्य सीपीयू द्वारा देखे गए मेमोरी रीड / राइट्स की अनुक्रम

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

सूत्रों का कहना है:


0

जब मैं 3 डी ग्राफिक्स और गेम इंजन विकास के लिए एक ऑनलाइन डाउनलोड करने योग्य वीडियो ट्यूटोरियल के माध्यम से काम कर रहा था, आधुनिक ओपनजीएल के साथ काम कर रहा था। हमने volatileअपनी कक्षाओं में से एक का उपयोग किया । ट्यूटोरियल वेबसाइट यहां पाई जा सकती है और volatileकीवर्ड के साथ काम करने वाला Shader Engineवीडियो श्रृंखला वीडियो 98 में पाया जाता है । ये कार्य मेरे स्वयं के नहीं हैं, लेकिन इसके लिए मान्यता प्राप्त हैं Marek A. Krzeminski, MAScऔर यह वीडियो डाउनलोड पृष्ठ का एक अंश है।

"चूंकि अब हम अपने गेम को कई थ्रेड्स में चला सकते हैं, इसलिए थ्रेड्स के बीच डेटा को ठीक से सिंक्रोनाइज़ करना महत्वपूर्ण है। इस वीडियो में मैं दिखाता हूं कि वॉलिटाइल वेरिएबल को सुनिश्चित करने के लिए वॉलिटाइल लॉकिंग क्लास कैसे बनाया जाए ..."

और अगर आप उसकी वेबसाइट के लिए सब्सक्राइब किए गए हैं और इस वीडियो के भीतर उसकी वीडियो तक पहुंच है, तो वह प्रोग्रामिंग के साथ इस लेख का संदर्भ देता है।Volatilemultithreading

यहाँ ऊपर दिए गए लिंक से लेख है: http://www.drdobbs.com/cpp/volatile-the-multithreaded-programmers-b/184403766

वाष्पशील: द मल्टीथ्रेडेड प्रोग्रामर बेस्ट फ्रेंड

आंद्रेई अलेक्जेंड्रेस्कु द्वारा, 01 फरवरी, 2001

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

मैं आपका मूड खराब नहीं करना चाहता, लेकिन यह कॉलम मल्टीथ्रेडेड प्रोग्रामिंग के खतरनाक विषय को संबोधित करता है। यदि - जैसा कि जेनेरिक की पिछली किस्त कहती है - अपवाद-सुरक्षित प्रोग्रामिंग कठिन है, यह मल्टीथ्रेडेड प्रोग्रामिंग की तुलना में बच्चे का खेल है।

कई थ्रेड्स का उपयोग करने वाले प्रोग्राम सामान्य रूप से लिखने, सही साबित करने, डिबग करने, बनाए रखने और सामान्य रूप से वश में करने के लिए कुख्यात हैं। गलत मल्टीथ्रेडेड प्रोग्राम एक गड़बड़ के बिना वर्षों तक चल सकते हैं, केवल अप्रत्याशित रूप से चलाने के लिए क्योंकि कुछ महत्वपूर्ण समय की शर्त को पूरा किया गया है।

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

बस एक छोटा सा खोजशब्द

यद्यपि थ्रेड्स की बात आती है, तो C और C ++ मानक दोनों स्पष्ट रूप से चुप हैं, वे अस्थिर कीवर्ड के रूप में मल्टीथ्रेडिंग के लिए थोड़ी रियायत करते हैं।

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

निम्नलिखित कोड पर विचार करें:

class Gadget {
public:
    void Wait() {
        while (!flag_) {
            Sleep(1000); // sleeps for 1000 milliseconds
        }
    }
    void Wakeup() {
        flag_ = true;
    }
    ...
private:
    bool flag_;
};

गैजेट का उद्देश्य :: ऊपर प्रतीक्षा करें हर सेकंड ध्वज_ सदस्य चर की जाँच करें और उस चर को दूसरे धागे द्वारा सही करने के लिए सेट किया गया है। कम से कम इसके प्रोग्रामर का इरादा है, लेकिन, अफसोस, प्रतीक्षा गलत है।

मान लीजिए कि संकलक आंकड़े बताते हैं कि स्लीप (1000) एक बाहरी पुस्तकालय में एक कॉल है जो संभवतः सदस्य चर flag_ को संशोधित नहीं कर सकता है। तब संकलक निष्कर्ष निकालता है कि यह फ्लैग_ को एक रजिस्टर में कैश कर सकता है और धीमी ऑन-बोर्ड मेमोरी तक पहुंचने के बजाय उस रजिस्टर का उपयोग कर सकता है। यह एकल-थ्रेडेड कोड के लिए एक उत्कृष्ट अनुकूलन है, लेकिन इस मामले में, यह शुद्धता को हानि पहुँचाता है: आप किसी गैजेट ऑब्जेक्ट के लिए प्रतीक्षा करने के बाद कॉल करते हैं, हालांकि एक अन्य थ्रेड को वेकअप कहते हैं, प्रतीक्षा हमेशा के लिए लूप करेगी। ऐसा इसलिए है क्योंकि फ्लैग_ का परिवर्तन उस रजिस्टर में परिलक्षित नहीं होगा जो फ्लैग_ को कैश करता है। अनुकूलन भी ... आशावादी है।

रजिस्टरों में कैशिंग चर बहुत मूल्यवान अनुकूलन है जो अधिकांश समय लागू होता है, इसलिए इसे बर्बाद करने के लिए एक दया होगी। C और C ++ आपको ऐसे कैशिंग को स्पष्ट रूप से अक्षम करने का मौका देता है। यदि आप किसी चर पर वाष्पशील संशोधक का उपयोग करते हैं, तो संकलक उस चर को रजिस्टर में कैश नहीं करेगा - प्रत्येक पहुंच उस चर की वास्तविक मेमोरी लोकेशन को हिट करेगा। तो आपको केवल गैजेट के इंतजार / वेकअप कॉम्बो काम करने के लिए करना होगा जो कि फ्लैग_ को उचित रूप से अर्हता प्राप्त करने के लिए है:

class Gadget {
public:
    ... as above ...
private:
    volatile bool flag_;
};

वाष्पीकरण के औचित्य और उपयोग के अधिकांश स्पष्टीकरण यहां बंद हो जाते हैं और आपको बहु-सूत्र में उपयोग किए जाने वाले आदिम प्रकारों को अस्थिर करने की सलाह देते हैं। हालाँकि, बहुत कुछ है जो आप अस्थिरता के साथ कर सकते हैं, क्योंकि यह C ++ की अद्भुत प्रकार प्रणाली का हिस्सा है।

उपयोगकर्ता-परिभाषित प्रकारों के साथ अस्थिरता का उपयोग करना

आप न केवल आदिम प्रकार, बल्कि उपयोगकर्ता-परिभाषित प्रकार भी अस्थिर-योग्य हो सकते हैं। उस स्थिति में, वाष्पशील एक प्रकार से कास्ट के समान होता है। (आप एक साथ एक ही प्रकार पर कांस्टेबल और वाष्पशील भी लागू कर सकते हैं।)

कास्ट के विपरीत, आदिम प्रकार और उपयोगकर्ता-परिभाषित प्रकारों के बीच अस्थिर भेदभाव होता है। सामान्य रूप से, कक्षाओं के विपरीत, आदिम प्रकार अभी भी अपने सभी संचालन (इसके अलावा, गुणा, असाइनमेंट, आदि) का समर्थन करते हैं जब अस्थिर-योग्य होते हैं। उदाहरण के लिए, आप एक गैर-वाष्पशील int को एक वाष्पशील int को असाइन कर सकते हैं, लेकिन आप एक गैर-वाष्पशील वस्तु को एक वाष्पशील वस्तु को असाइन नहीं कर सकते।

आइए उदाहरण के आधार पर उपयोगकर्ता-परिभाषित प्रकारों पर वाष्पशील कैसे काम करता है, इसका उदाहरण दें।

class Gadget {
public:
    void Foo() volatile;
    void Bar();
    ...
private:
    String name_;
    int state_;
};
...
Gadget regularGadget;
volatile Gadget volatileGadget;

यदि आपको लगता है कि अस्थिर वस्तुओं के साथ उपयोगी नहीं है, तो कुछ आश्चर्य के लिए तैयार करें।

volatileGadget.Foo(); // ok, volatile fun called for
                  // volatile object
regularGadget.Foo();  // ok, volatile fun called for
                  // non-volatile object
volatileGadget.Bar(); // error! Non-volatile function called for
                  // volatile object!

गैर-योग्य प्रकार से इसके वाष्पशील प्रतिरूप में रूपांतरण तुच्छ है। हालाँकि, कास्ट के साथ के रूप में, आप यात्रा को अस्थिर से गैर-योग्य तक वापस नहीं कर सकते। आपको एक कास्ट का उपयोग करना चाहिए:

Gadget& ref = const_cast<Gadget&>(volatileGadget);
ref.Bar(); // ok

एक अस्थिर-योग्य वर्ग केवल अपने इंटरफ़ेस के सबसेट तक पहुँच देता है, एक उपसमूह जो वर्ग कार्यान्वयनकर्ता के नियंत्रण में है। उपयोगकर्ता केवल एक const_cast का उपयोग करके उस प्रकार के इंटरफ़ेस तक पूर्ण पहुंच प्राप्त कर सकते हैं। इसके अलावा, कब्ज की तरह, अस्थिरता कक्षा से अपने सदस्यों तक फैलती है (उदाहरण के लिए, volatileGadget.name_ और volatileGadget.state_ अस्थिर संस्करण हैं)।

अस्थिर, क्रिटिकल सेक्शन और रेस की स्थिति

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

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

अनुभवी मल्टीथ्रेडेड प्रोग्रामर ने ऊपर दिए गए दो पैराग्राफ को पढ़ते हुए जम्हाई ली होगी, लेकिन उनका उद्देश्य एक बौद्धिक कसरत प्रदान करना है, क्योंकि अब हम अस्थिर कनेक्शन के साथ जुड़ेंगे। हम इसे C ++ प्रकार की दुनिया और थ्रेडिंग शब्दार्थ दुनिया के बीच एक समानांतर ड्राइंग द्वारा करते हैं।

  • एक महत्वपूर्ण खंड के बाहर, कोई भी धागा किसी भी समय किसी अन्य को बाधित कर सकता है; कोई नियंत्रण नहीं है, इसलिए परिणामस्वरूप कई थ्रेड से सुलभ चर अस्थिर हैं। यह वाष्पशील के मूल इरादे को ध्यान में रखते हुए है - जो कि संकलक को एक बार में कई थ्रेड द्वारा उपयोग किए जाने वाले अनजाने कैशिंग मूल्यों से रोकना है।
  • म्यूटेक्स द्वारा परिभाषित एक महत्वपूर्ण खंड के अंदर, केवल एक धागे तक पहुंच है। नतीजतन, एक महत्वपूर्ण खंड के अंदर, निष्पादन कोड में एकल-थ्रेडेड शब्दार्थ है। नियंत्रित चर अब अस्थिर नहीं है - आप अस्थिर गुणक को हटा सकते हैं।

संक्षेप में, थ्रेड्स के बीच साझा किया गया डेटा एक महत्वपूर्ण अनुभाग के बाहर वैचारिक रूप से अस्थिर है, और एक महत्वपूर्ण अनुभाग के अंदर गैर-अस्थिर है।

आप म्यूटेक्स को लॉक करके एक महत्वपूर्ण अनुभाग दर्ज करते हैं। आप const_cast को लागू करके एक प्रकार से वाष्पशील क्वालिफायर निकालते हैं। यदि हम इन दोनों ऑपरेशनों को एक साथ करने का प्रबंधन करते हैं, तो हम C ++ के टाइप सिस्टम और एप्लिकेशन के थ्रेडिंग शब्दार्थों के बीच संबंध बनाते हैं। हम अपने लिए कंपाइलर चेक रेस की स्थिति बना सकते हैं।

LockingPtr

हमें एक उपकरण की आवश्यकता है जो एक म्यूटेक्स अधिग्रहण और एक const_cast एकत्र करता है। चलिए एक LockingPtr क्लास टेम्पलेट विकसित करते हैं, जिसे आप एक वाष्पशील वस्तु obj और एक म्यूटेक्स mtx के साथ आरंभ करते हैं। अपने जीवनकाल के दौरान, एक LockingPtr mtx का अधिग्रहण करता है। इसके अलावा, LockingPtr अस्थिर-पट्टी वाले obj तक पहुंच प्रदान करता है। एक्सेस को एक स्मार्ट पॉइंटर फैशन में, ऑपरेटर-> और ऑपरेटर * के माध्यम से पेश किया जाता है। Const_cast LockingPtr के अंदर किया जाता है। कास्ट शब्दबद्ध रूप से मान्य है क्योंकि लॉकिंगप्रट अपने जीवनकाल के लिए म्यूटेक्स का अधिग्रहण करता है।

सबसे पहले, एक वर्ग Mutex के कंकाल को परिभाषित करें जिसके साथ LockingPtr काम करेगा:

class Mutex {
public:
    void Acquire();
    void Release();
    ...    
};

LockingPtr का उपयोग करने के लिए, आप अपने ऑपरेटिंग सिस्टम के मूल डेटा संरचनाओं और आदिम कार्यों का उपयोग करके म्यूटेक्स को लागू करते हैं।

LockingPtr को नियंत्रित चर के प्रकार के साथ जोड़ दिया जाता है। उदाहरण के लिए, यदि आप किसी विजेट को नियंत्रित करना चाहते हैं, तो आप लॉकिंगप्रट का उपयोग करते हैं जिसे आप टाइप वाष्पशील विजेट के एक चर के साथ आरंभ करते हैं।

LockingPtr की परिभाषा बहुत सरल है। LockingPtr एक अपरिष्कृत स्मार्ट पॉइंटर को लागू करता है। यह केवल एक const_cast और एक महत्वपूर्ण अनुभाग एकत्र करने पर केंद्रित है।

template <typename T>
class LockingPtr {
public:
    // Constructors/destructors
    LockingPtr(volatile T& obj, Mutex& mtx)
      : pObj_(const_cast<T*>(&obj)), pMtx_(&mtx) {    
        mtx.Lock();    
    }
    ~LockingPtr() {    
        pMtx_->Unlock();    
    }
    // Pointer behavior
    T& operator*() {    
        return *pObj_;    
    }
    T* operator->() {   
        return pObj_;   
    }
private:
    T* pObj_;
    Mutex* pMtx_;
    LockingPtr(const LockingPtr&);
    LockingPtr& operator=(const LockingPtr&);
};

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

मान लें कि आपके पास दो धागे हैं जो एक वेक्टर वस्तु साझा करते हैं:

class SyncBuf {
public:
    void Thread1();
    void Thread2();
private:
    typedef vector<char> BufT;
    volatile BufT buffer_;
    Mutex mtx_; // controls access to buffer_
};

एक थ्रेड फ़ंक्शन के अंदर, आप बफर_ सदस्य चर पर नियंत्रित पहुंच प्राप्त करने के लिए लॉकिंगप्रट का उपयोग करते हैं:

void SyncBuf::Thread1() {
    LockingPtr<BufT> lpBuf(buffer_, mtx_);
    BufT::iterator i = lpBuf->begin();
    for (; i != lpBuf->end(); ++i) {
        ... use *i ...
    }
}

कोड लिखना और समझना बहुत आसान है - जब भी आपको बफर_ का उपयोग करने की आवश्यकता होती है, तो आपको इसे इंगित करते हुए एक लॉकिंगप्राट बनाना होगा। एक बार जब आप ऐसा करते हैं, तो आपके पास वेक्टर के संपूर्ण इंटरफ़ेस तक पहुंच होती है।

अच्छा हिस्सा यह है कि यदि आप कोई गलती करते हैं, तो संकलक इसे इंगित करेगा:

void SyncBuf::Thread2() {
    // Error! Cannot access 'begin' for a volatile object
    BufT::iterator i = buffer_.begin();
    // Error! Cannot access 'end' for a volatile object
    for ( ; i != lpBuf->end(); ++i ) {
        ... use *i ...
    }
}

जब तक आप या तो एक const_cast लागू नहीं करते हैं या LockingPtr का उपयोग करते हैं, आप बफर_ के किसी भी फ़ंक्शन तक नहीं पहुंच सकते हैं। अंतर यह है कि LockingPtr, अस्थिर चर के लिए const_cast लागू करने का आदेश दिया तरीका प्रदान करता है।

LockPPtr उल्लेखनीय रूप से अभिव्यंजक है। यदि आपको केवल एक फ़ंक्शन को कॉल करने की आवश्यकता है, तो आप एक अनाम अस्थायी LockingPtr ऑब्जेक्ट बना सकते हैं और इसे सीधे उपयोग कर सकते हैं:

unsigned int SyncBuf::Size() {
return LockingPtr<BufT>(buffer_, mtx_)->size();
}

वापस आदिम प्रकार के लिए

हमने देखा कि कैसे निर्बाध रूप से अनियंत्रित पहुंच के खिलाफ वस्तुओं की सुरक्षा करता है और लॉकिंगपार्ट धागा-सुरक्षित कोड लिखने का एक सरल और प्रभावी तरीका प्रदान करता है। आइए अब आदिम प्रकारों पर लौटते हैं, जिनका व्यवहार भिन्न-भिन्न प्रकार से किया जाता है।

आइए एक उदाहरण पर विचार करें जहां कई थ्रेड्स प्रकार के चर को साझा करते हैं int।

class Counter {
public:
    ...
    void Increment() { ++ctr_; }
    void Decrement() { —ctr_; }
private:
    int ctr_;
};

अगर वृद्धि और विकृति को विभिन्न थ्रेड्स से बुलाया जाना है, तो ऊपर का टुकड़ा छोटी गाड़ी है। सबसे पहले, ctr_ को अस्थिर होना चाहिए। दूसरा, यहां तक ​​कि एक प्रतीत होता है परमाणु ऑपरेशन जैसे कि ++ ctr_ वास्तव में एक तीन चरण का ऑपरेशन है। मेमोरी में स्वयं कोई अंकगणित क्षमताएं नहीं हैं। जब एक चर बढ़ाना, प्रोसेसर:

  • उस चर को एक रजिस्टर में पढ़ता है
  • रजिस्टर में मूल्य बढ़ाता है
  • परिणाम को वापस स्मृति में लिखता है

इस तीन-चरण ऑपरेशन को आरएमडब्ल्यू (रीड-मॉडिफाई-राइट) कहा जाता है। आरएमडब्ल्यू ऑपरेशन के संशोधित हिस्से के दौरान, अधिकांश प्रोसेसर मेमोरी बस को मुफ्त देते हैं ताकि अन्य प्रोसेसर को मेमोरी तक पहुंच प्रदान की जा सके।

यदि उस समय एक और प्रोसेसर एक ही चर पर आरएमडब्ल्यू ऑपरेशन करता है, तो हमारे पास एक दौड़ की स्थिति है: दूसरा लेखन पहले के प्रभाव को ओवरराइट करता है।

उससे बचने के लिए, आप फिर से, LockingPtr पर भरोसा कर सकते हैं:

class Counter {
public:
    ...
    void Increment() { ++*LockingPtr<int>(ctr_, mtx_); }
    void Decrement() { —*LockingPtr<int>(ctr_, mtx_); }
private:
    volatile int ctr_;
    Mutex mtx_;
};

अब कोड सही है, लेकिन SyncBuf के कोड की तुलना में इसकी गुणवत्ता हीन है। क्यों? क्योंकि काउंटर के साथ, कंपाइलर आपको चेतावनी नहीं देगा यदि आप गलती से ctr_ को सीधे एक्सेस कर रहे हैं (इसे लॉक किए बिना)। संकलक ++ ctr_ अगर ctr_ अस्थिर है, तो उत्पन्न कोड बस गलत है। संकलक अब आपका सहयोगी नहीं है, और केवल आपका ध्यान आपको दौड़ की स्थिति से बचने में मदद कर सकता है।

तब आपको क्या करना चाहिए? बस उन प्राइमिटिव डेटा को एनकैप्सुलेट करें जो आप उच्च-स्तरीय संरचनाओं में उपयोग करते हैं और उन संरचनाओं के साथ अस्थिरता का उपयोग करते हैं। विरोधाभासी रूप से, यह अंतर्निहित के साथ सीधे अस्थिर का उपयोग करने के लिए बदतर है, इस तथ्य के बावजूद कि शुरू में यह अस्थिर का उपयोग इरादा था!

अस्थिर सदस्य कार्य

अब तक, हमारे पास ऐसी कक्षाएं हैं जो अस्थिर डेटा सदस्यों को एकत्रित करती हैं; अब चलो कक्षाओं को डिजाइन करने के बारे में सोचते हैं जो बदले में बड़ी वस्तुओं का हिस्सा होंगे और थ्रेड्स के बीच साझा किए जाएंगे। यहां वह जगह है जहां अस्थिर सदस्य कार्य बहुत मदद कर सकते हैं।

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

उदाहरण के लिए, आप एक वर्ग विजेट को परिभाषित करते हैं जो एक ऑपरेशन को दो वेरिएंट में लागू करता है - एक थ्रेड-सेफ एक और एक तेज़, असुरक्षित एक।

class Widget {
public:
    void Operation() volatile;
    void Operation();
    ...
private:
    Mutex mtx_;
};

ओवरलोडिंग के उपयोग पर ध्यान दें। अब विजेट का उपयोगकर्ता अस्थिर वस्तुओं के लिए एक समान सिंटैक्स का उपयोग करके ऑपरेशन को आमंत्रित कर सकता है और थ्रेड सुरक्षा प्राप्त कर सकता है, या नियमित वस्तुओं के लिए और गति प्राप्त कर सकता है। उपयोगकर्ता को साझा विजेट ऑब्जेक्ट को अस्थिर के रूप में परिभाषित करने के बारे में सावधान रहना चाहिए।

एक अस्थिर सदस्य फ़ंक्शन को लागू करते समय, पहला ऑपरेशन आमतौर पर इसे लॉकिंगपार्ट के साथ लॉक करना होता है। तब काम गैर-अस्थिर सिबलिंग का उपयोग करके किया जाता है:

void Widget::Operation() volatile {
    LockingPtr<Widget> lpThis(*this, mtx_);
    lpThis->Operation(); // invokes the non-volatile function
}

सारांश

जब मल्टीथ्रेडेड प्रोग्राम लिखते हैं, तो आप अपने लाभ के लिए अस्थिर का उपयोग कर सकते हैं। आपको निम्नलिखित नियमों पर चलना चाहिए:

  • सभी साझा किए गए ऑब्जेक्ट को अस्थिर के रूप में परिभाषित करें।
  • आदिम प्रकारों के साथ सीधे वाष्पशील का उपयोग न करें।
  • साझा कक्षाओं को परिभाषित करते समय, धागा सुरक्षा को व्यक्त करने के लिए अस्थिर सदस्य कार्यों का उपयोग करें।

यदि आप ऐसा करते हैं, और यदि आप साधारण जेनेरिक कंपोनेंट LockingPtr का उपयोग करते हैं, तो आप थ्रेड-सेफ कोड लिख सकते हैं और दौड़ की स्थिति के बारे में बहुत कम चिंता कर सकते हैं, क्योंकि कंपाइलर आपके लिए चिंता करेगा और उन स्थानों पर परिश्रम से इंगित करेगा जहां आप गलत हैं।

कुछ परियोजनाओं को मैंने बड़े प्रभाव से अस्थिर और लॉकिंगप्रेट के उपयोग के साथ जोड़ा है। कोड साफ और समझ में आता है। मैं कुछ गतिरोधों को याद करता हूं, लेकिन मैं दौड़ की स्थिति के लिए गतिरोध पसंद करता हूं क्योंकि वे डिबग करना बहुत आसान हैं। वस्तुतः नस्ल की स्थितियों से संबंधित कोई समस्या नहीं थी। लेकिन तब आपको कभी पता नहीं चलता।

स्वीकृतियाँ

जेम्स कान्जे और सोरिन जियानू के लिए बहुत धन्यवाद जिन्होंने व्यावहारिक विचारों के साथ मदद की।


आंद्रेई अलेक्जेंड्रेस्कु रियलनेटवर्क इंक (www.realnetworks.com) में एक विकास प्रबंधक है, जो सिएटल, वाशिंगटन में स्थित है, और प्रशंसित पुस्तक मॉडर्न सी ++ डिजाइन के लेखक हैं। उनसे www.moderncppdesign.com पर संपर्क किया जा सकता है। आंद्रेई भी C ++ सेमिनार (www.gotw.ca/cpp_seminar) के चुनिंदा प्रशिक्षकों में से एक हैं।

यह लेख थोड़ा दिनांकित हो सकता है, लेकिन यह हमारे लिए दौड़ की शर्तों के लिए संकलक जाँच करते समय घटनाओं को अतुल्यकालिक रखने में मदद करने के लिए मल्टीथ्रेडेड प्रोग्रामिंग के उपयोग के साथ वाष्पशील संशोधक के उपयोग के एक उत्कृष्ट उपयोग के लिए अच्छी जानकारी देता है। यह सीधे ओप्स के मूल प्रश्न का उत्तर नहीं दे सकता है मेमोरी बाड़ बनाने के बारे में, लेकिन मैं इसे दूसरों के लिए एक उत्तर के रूप में पोस्ट करने का चयन करता हूं, जब मल्टीथ्रेडेड एप्लिकेशन के साथ काम करते हुए अस्थिरता के अच्छे उपयोग के लिए एक उत्कृष्ट संदर्भ।


0

volatileअनिवार्य रूप से कीवर्ड का मतलब है कि किसी ऑब्जेक्ट को पढ़ना और लिखना ठीक उसी तरह से किया जाना चाहिए जैसा कि प्रोग्राम द्वारा लिखा गया है, और किसी भी तरह से अनुकूलित नहीं है । बाइनरी कोड को सी या सी ++ कोड का पालन करना चाहिए: एक लोड जहां यह पढ़ा जाता है, एक स्टोर जहां एक लेखन होता है।

इसका यह भी अर्थ है कि किसी भी रीड को अनुमानित मूल्य के परिणाम की उम्मीद नहीं की जानी चाहिए: कंपाइलर को एक वाष्पशील वस्तु के लिए लिखने के तुरंत बाद एक रीड के बारे में कुछ भी ग्रहण नहीं करना चाहिए:

volatile int i;
i = 1;
int j = i; 
if (j == 1) // not assumed to be true

volatile"C एक उच्च स्तरीय असेंबली भाषा है" टूलबॉक्स में सबसे महत्वपूर्ण उपकरण हो सकता है ।

क्या ऑब्जेक्ट को अस्थिर घोषित करना कोड के व्यवहार को सुनिश्चित करने के लिए पर्याप्त है जो अतुल्यकालिक परिवर्तनों से संबंधित है, प्लेटफ़ॉर्म पर निर्भर करता है: विभिन्न सीपीयू सामान्य मेमोरी रीड और राइट के लिए गारंटीकृत सिंक्रनाइज़ेशन के विभिन्न स्तर देते हैं। जब तक आप इस क्षेत्र के विशेषज्ञ नहीं होते हैं, तब तक शायद आपको ऐसे निम्न स्तर के मल्टीथ्रेडिंग कोड लिखने की कोशिश नहीं करनी चाहिए।

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.