2013-12-11 21 views
229

मेरे पास दो उपयोग के मामले हैं।std :: unique_lock <std::mutex> या std :: lock_guard <std::mutex>?

ए। मैं दो धागे से कतार में पहुंच को सिंक्रनाइज़ करना चाहता हूं।

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

उपयोग केस के लिए मैं std::lock_guard<> का उपयोग कर कोड उदाहरण देखता हूं। उपयोग केस बी के लिए मैं std::unique_lock<> का उपयोग कर कोड उदाहरण देखता हूं।

दोनों के बीच क्या अंतर है और मुझे किस मामले का उपयोग करना चाहिए?

उत्तर

222

अंतर यह है कि आप std::unique_lock को लॉक और अनलॉक कर सकते हैं। std::lock_guard निर्माण पर केवल एक बार बंद कर दिया जाएगा और विनाश पर अनलॉक किया जाएगा।

तो उपयोगकेस बी के लिए आपको निश्चित रूप से std::unique_lock की स्थिति चर के लिए आवश्यकता है। यदि ए यह निर्भर करता है कि आपको गार्ड को फिर से खोलना है या नहीं।

std::unique_lock में अन्य विशेषताएं हैं जो इसे अन्य की अनुमति देती हैं: म्यूटेक्स को तुरंत लॉक किए बिना बनाया जाए लेकिन आरएआईआई रैपर बनाने के लिए बनाया जाए (here देखें)।

std::lock_guard एक सुविधाजनक आरएआईआई रैपर भी प्रदान करता है, लेकिन कई म्यूटेक्स को सुरक्षित रूप से लॉक नहीं कर सकता है। यह प्रयोग किया जा सकता है जब आप एक सीमित दायरे के लिए एक आवरण, उदा .: एक सदस्य समारोह की जरूरत है:

class MyClass{ 
    std::mutex my_mutex; 
    void member_foo() { 
     std::lock_guard<mutex_type> lock(this->my_mutex);    
     /* 
     block of code which needs mutual exclusion (e.g. open the same 
     file in multiple threads). 
     */ 

     //mutex is automatically released when lock goes out of scope   
}; 

chmike द्वारा एक प्रश्न को स्पष्ट करने के लिए, डिफ़ॉल्ट std::lock_guard द्वारा और std::unique_lock ही हैं। तो उपर्युक्त मामले में, आप को std::unique_lock से प्रतिस्थापित कर सकते हैं। हालांकि, std::unique_lock में एक टैड अधिक ओवरहेड हो सकता है।

+1

निर्देश के साथ std :: unique_lock लॉक (myMutex); म्यूटेक्स को कन्स्ट्रक्टर द्वारा लॉक किया जाएगा? – chmike

+2

@chmike हाँ, यह होगा। कुछ स्पष्टीकरण जोड़ा गया। – inf

+0

यदि मैं केस ए में उपयोग कर रहा हूं, तो क्या अद्वितीय_लॉक के बजाय lock_gard का उपयोग करना अधिक कुशल होगा? – chmike

31

lock_guard का उपयोग करें जब तक आपको lock को नष्ट किए बिना म्यूटेक्स मैन्युअल रूप से सक्षम करने की आवश्यकता हो।

विशेष रूप से condition_variablewait पर कॉल पर सोने के लिए अपने म्यूटेक्स को अनलॉक करता है। यही कारण है कि lock_guard यहां पर्याप्त नहीं है।

+0

सशर्त चर के प्रतीक्षा विधियों में से एक को लॉक_गार्ड पास करना ठीक होगा क्योंकि किसी भी कारण से प्रतीक्षा समाप्त होने पर म्यूटेक्स हमेशा पुनः प्राप्त होता है। हालांकि मानक केवल अद्वितीय_लॉक के लिए एक इंटरफ़ेस प्रदान करता है। इसे मानक में कमी के रूप में माना जा सकता है। –

+2

@ क्रिस आप अभी भी इस मामले में encapsulation तोड़ देंगे। प्रतीक्षा विधि को 'lock_guard' से mutex निकालने और इसे अनलॉक करने में सक्षम होने की आवश्यकता होगी, इस प्रकार अस्थायी रूप से गार्ड के वर्ग आविष्कार को तोड़ दिया जा सकता है। भले ही यह उपयोगकर्ता के लिए अदृश्य हो, फिर भी मैं इस मामले में 'lock_guard' के उपयोग की अनुमति न देने का वैध कारण मानूंगा। – ComicSansMS

+0

यदि ऐसा है, तो यह अदृश्य और ज्ञानी नहीं होगा। जीसीसी -4.8 यह करता है। प्रतीक्षा करें (unique_lock और) __gthread_cond_wait (& _ M_cond, __lock.mutex() -> native_handle()) (libstdC++ - v3/src/C++ 11/condition_variable.cc) देखें, जो pthread_cond_wait() को कॉल करता है (libgcc/gthr देखें -posix.h)। लॉक_गार्ड के लिए भी ऐसा किया जा सकता है (लेकिन ऐसा नहीं है क्योंकि यह condition_variable के लिए मानक में नहीं है)। –

73

lock_guard और unique_lock काफी समान हैं; lock_guard सीमित इंटरफ़ेस वाला एक प्रतिबंधित संस्करण है।

lock_guard हमेशा इसके निर्माण से इसके विनाश के लिए एक ताला रखता है। unique_lock तुरंत लॉक किए बिना बनाया जा सकता है, इसके अस्तित्व में किसी भी बिंदु पर अनलॉक कर सकता है, और लॉक के स्वामित्व को एक उदाहरण से दूसरे में स्थानांतरित कर सकता है।

तो आप हमेशा lock_guard का उपयोग करते हैं, जब तक कि आपको unique_lock की क्षमताओं की आवश्यकता न हो। condition_variable को unique_lock की आवश्यकता है।

+4

'एक condition_variable को एक unique_lock की आवश्यकता है।' - हाँ _but_ केवल 'प्रतीक्षा() 'आईएनजी पक्ष पर, जैसा कि मेरे बारे में विस्तारित है इंफ को टिप्पणी करें। –

-6

जैसा कि दूसरों द्वारा उल्लेख किया गया है, std :: unique_lock mutex की लॉक स्थिति को ट्रैक करता है, ताकि आप लॉक के निर्माण के बाद लॉकिंग को रोक सकें, और लॉक के विनाश से पहले अनलॉक कर सकें। std :: lock_guard यह अनुमति नहीं देता है।

ऐसा कोई कारण नहीं है कि std :: condition_variable प्रतीक्षा फ़ंक्शंस को लॉक_गार्ड और साथ ही एक अद्वितीय_लॉक नहीं लेना चाहिए, क्योंकि जब भी प्रतीक्षा समाप्त होती है (किसी भी कारण से) mutex स्वचालित रूप से पुनः प्राप्त होता है जिससे कोई अर्थपूर्ण उल्लंघन नहीं होता है । हालांकि मानक के मुताबिक, एक स्टेटस वैरिएबल के साथ std :: lock_guard का उपयोग करने के लिए आपको std :: condition_variable के बजाय std :: condition_variable_any का उपयोग करना होगा।

संपादित करें: हटाया गया है "pthreads इंटरफ़ेस std :: condition_variable और std :: condition_variable_any का उपयोग समान होना चाहिए"। जीसीसी के कार्यान्वयन को देखकर पर:

  • std :: condition_variable :: प्रतीक्षा (std :: unique_lock &) बस म्युटेक्स unique_lock द्वारा आयोजित (के संबंध में pthread_cond_wait() अंतर्निहित pthread हालत चर पर कॉल करता है और इसलिए समान रूप से कर सकते थे lock_guard के लिए ऐसा ही करें, लेकिन ऐसा नहीं है क्योंकि मानक उस के लिए प्रदान नहीं करता है)
  • std :: condition_variable_any किसी भी लॉक करने योग्य ऑब्जेक्ट के साथ काम कर सकता है, जिसमें एक म्यूटेक्स लॉक भी नहीं है (इसलिए यह भी काम कर सकता है एक इंटर-प्रोसेस सेमेफोर के साथ)
7

कुछ सामान्य चीजें हैं lock_guard और unique_lock और कुछ अंतर के बीच।

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

यह घटना lock_guard के सिद्धांत के खिलाफ है। lock_guard केवल एक बार बनाया जा सकता है और केवल एक बार नष्ट हो सकता है।

इसलिए lock_guard एक शर्त चर साथ संयोजन में उपयोग नहीं किया जा सकता है, लेकिन एक unique_lock हो सकता है (क्योंकि unique_lock बंद किया जा सकता है और वह अनलॉक कई बार)।

+4

'वह कंपाइलर एक कंडीशन वैरिएबल के साथ संयोजन में लॉक_गार्ड का उपयोग करने की अनुमति नहीं देता है' यह गलत है। यह निश्चित रूप से ** ** 'सूचित करें()' आईएनजी पक्ष पर 'lock_guard' के साथ पूरी तरह से अनुमति देता है और काम करता है। केवल 'प्रतीक्षा() 'int पक्ष को' unique_lock' की आवश्यकता होती है, क्योंकि स्थिति की जांच करते समय 'प्रतीक्षा()' को लॉक जारी करना होगा। –

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