यह देखते हुए कि m
प्रकार std::mutex
की एक चर रहा है:
ख से एक का काम:
int a;
m.lock();
b += 1;
a = b;
m.unlock();
do_something_with(a);
वहाँ पर यहाँ जा रहा एक 'स्पष्ट' बात यह है:
इस क्रम कल्पना कीजिए और ख की वेतन वृद्धि अन्य धागे से हस्तक्षेप से 'सुरक्षित' है, क्योंकि अन्य थ्रेड एक ही m
लॉक करने के लिए जब तक हम m.unlock()
फोन का प्रयास करेंगे और अवरुद्ध हो जाएगा।
और एक और सूक्ष्म चीज चल रही है।
सिंगल-थ्रेडेड कोड में, कंपाइलर लोड और स्टोर्स को फिर से ऑर्डर करना चाहता है।
int a = b + 1;
// m.lock();
b = a;
// m.unlock();
do_something_with(a);
या भी:
do_something_with(++b);
हालांकि, std::mutex::lock()
, unlock()
ताले बिना, संकलक प्रभावी रूप से अपने कोड को फिर से लिखना करने के लिए स्वतंत्र है कि अगर यह पता चला अपने चिपसेट पर अधिक कुशल होने का होगा , std::thread()
, std::async()
, std::future::get()
और इसी तरह बाड़ हैं। कंपाइलर 'जानता है' कि यह लोड और स्टोर्स (पढ़ता है और लिखता है) इस तरह से पुन: व्यवस्थित नहीं कर सकता है कि ऑपरेशन बाड़ के दूसरी तरफ समाप्त होता है जहां से आपने कोड लिखा था।
1:
2: m.lock(); <--- This is a fence
3: b += 1; <--- So this load/store operation may not move above line 2
4: m.unlock(); <-- Nor may it be moved below this line
कल्पना कीजिए कि अगर मामला यह नहीं था क्या होगा:
(पुनर्क्रमित कोड)
thread1: int a = b + 1;
<--- Here another thread precedes us and executes the same block of code
thread2: int a = b + 1;
thread2: m.lock();
thread2: b = a;
thread2: m.unlock();
thread1: m.lock();
thread1: b = a;
thread1: m.unlock();
thread1:do_something_with(a);
thread2:do_something_with(a);
आप इसे माध्यम से पालन करें, तो आप देखेंगे कि ख अब गलत है इसमें मूल्य, क्योंकि संकलक आपके कोड को तेज़ी से बनाने के लिए तैयार था।
... और यह केवल संकलक अनुकूलन है। std::mutex
, इत्यादि मेमोरी कैश को एक अधिक 'इष्टतम' तरीके से रीडरिंग लोड और स्टोर्स से रोकता है, जो एकल-थ्रेडेड वातावरण में ठीक होगा लेकिन बहु-कोर (यानी कोई आधुनिक पीसी या फोन) सिस्टम में विनाशकारी होगा।
इस सुरक्षा के लिए एक लागत है, क्योंकि थ्रेड ए के कैश को उसी डेटा को पढ़ने से पहले फ़्लश किया जाना चाहिए, और कैश मेमोरी एक्सेस की तुलना में स्मृति में कैश फ्लश करना धीमा है। लेकिन सीएस्ट ला वी। समवर्ती निष्पादन को सुरक्षित करने का यही एकमात्र तरीका है।
यही कारण है कि हम एक एसएमपी प्रणाली में, यदि संभव हो, तो पसंद करते हैं, प्रत्येक थ्रेड में डेटा की अपनी प्रतिलिपि होती है जिस पर काम करना है। हम न केवल लॉक में बिताए गए समय को कम करना चाहते हैं, बल्कि कई बार बाड़ पार करते हैं।
मैं std::memory_order
संशोधक के बारे में बात करने के लिए जा सकते हैं, लेकिन यह एक अंधेरे और खतरनाक छेद है, जो विशेषज्ञों अक्सर गलत और जिसमें शुरुआती यह सही हो रही है की कोई आशा नहीं है।
mutex किसी भी चर को लॉक नहीं करता है, यह केवल उस धागे को अवरुद्ध करता है जो इसे प्राप्त करने का प्रयास कर रहा है।यदि वे प्रोग्राम केवल 'लॉक()' और 'अनलॉक()' कॉल के बीच पहुंचता है तो इसका चर बदलने की प्रभाव पड़ती है। कोड में म्यूटेक्स 'लॉक()' और 'अनलॉक() 'की मौजूदगी भी कंपाइलर को चर * एक्सेस * की स्मृति मेमोरी एक्सेस को फिर से ऑर्डर करने से रोकती है। –
आपको यह सुनिश्चित करने की भी आवश्यकता है कि धागे एक ही म्यूटेक्स ऑब्जेक्ट का उपयोग करें। प्रत्येक थ्रेड में एक अलग म्यूटेक्स होने से कुछ भी नहीं होगा। इसके बजाए यह ब्लॉक करता है अगर कोई अन्य थ्रेड पहले से लॉक किए गए म्यूटेक्स को लॉक करने की कोशिश करता है जैसे कि @ रिचर्डहॉज वर्णित है। – Hayt
धन्यवाद, यह वास्तव में इसे मंजूरी दे दी! – niko