std :: unique_lock <std :: mutex> या std :: lock_guard <std :: mutex>?


349

मेरे पास दो उपयोग के मामले हैं।

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

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

उपयोग के मामले के लिए AI कोड का उपयोग करके देखें std::lock_guard<>। उपयोग के मामले के लिए BI कोड का उपयोग करके उदाहरण देखें std::unique_lock<>

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

जवाबों:


344

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

इसलिए केस बी के उपयोग के लिए आपको निश्चित रूप std::unique_lockसे कंडीशन वैरिएबल की जरूरत है । मामले में ए यह निर्भर करता है कि आपको गार्ड को अनलॉक करने की आवश्यकता है या नहीं।

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

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

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::lock_guardसाथ बदल सकते हैं std::unique_lock। हालांकि, std::unique_lockएक बालक अधिक उपरि हो सकता है।

ध्यान दें कि इन दिनों के std::scoped_lockबजाय एक का उपयोग करना चाहिए std::lock_guard


2
निर्देश std के साथ :: unique_lock <std :: mutex> lock (myMutex); क्या कंस्ट्रक्टर द्वारा म्यूटेक्स को लॉक किया जाएगा?
चमीक

3
@chmike हाँ, यह होगा। कुछ स्पष्टीकरण जोड़ा।
स्टीफन डॉलबर्ग

10
@chmike खैर, मुझे लगता है कि यह कार्यक्षमता की तुलना में दक्षता का सवाल कम है। यदि std::lock_guardआपके केस ए के लिए पर्याप्त है, तो आपको इसका उपयोग करना चाहिए। यह न केवल अनावश्यक ओवरहेड से बचाता है, बल्कि पाठक को यह भी दिखाता है कि आप इस गार्ड को कभी भी अनलॉक नहीं करेंगे।
स्टीफन डॉलबर्ग

5
@chmike: सैद्धांतिक रूप से हाँ। हालांकि म्यूट बिल्कुल हल्के निर्माण नहीं हैं, इसलिए अतिरिक्त ओवरहेड को unique_lockवास्तव में म्यूटेक्स को लॉक करने और अनलॉक करने की लागत से बौना होने की संभावना है (यदि कंपाइलर ने उस ओवरहेड को दूर नहीं किया, जो संभव हो सकता है)।
ग्रिजली

6
So for usecase B you definitely need a std::unique_lock for the condition variable- हाँ, लेकिन केवल उस थ्रेड में cv.wait(), क्योंकि वह विधि एटमेटिक रूप से म्यूटेक्स को छोड़ती है। दूसरे धागे में जहां आप साझा चर (ओं) को अपडेट करते हैं और फिर कॉल करते हैं cv.notify_one(), lock_guardम्यूटेक्स-इन-स्कोप को लॉक करने के लिए एक सरल घुटन ... जब तक आप कुछ और विस्तृत नहीं कर रहे हैं जो मैं कल्पना नहीं कर सकता! उदाहरण के लिए en.cppreference.com/w/cpp/thread/condition_variable - मेरे लिए काम करता है :)
अंडरस्कोर_ड

115

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

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

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


11
A condition_variable needs a unique_lock.- हाँ, लेकिन केवल wait()आईएनजी की ओर, जैसा कि मेरी टिप्पणी में विस्तार से बताया गया है।
अंडरस्कोर_ड

48

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

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


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

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

यदि हां, तो यह अदृश्य और अनिर्वचनीय होगा। gcc-4.8 करता है। प्रतीक्षा करें (unique_lock <mutex> &) कॉल __gthread_cond_wait (& _ M_cond, __lock.mutex () -> native_handle ()) (libstdc ++ देखें - v3 / src / c ++ 11 / condition_variable.cc), जिसे pthread_cond_wait_it कहते हैं। /gthr-posix.h)। वही लॉक_गार्ड के लिए किया जा सकता है (लेकिन ऐसा इसलिए नहीं है क्योंकि यह कंडीशन के लिए मानक नहीं है)।
क्रिस वाइन

4
@ क्रिस यह बिंदु lock_guardअंतर्निहित म्यूटेक्स को पुनः प्राप्त करने की अनुमति नहीं देता है। कोड के बारे में सरल तर्क का उपयोग करने की अनुमति देने के लिए यह एक जानबूझकर सीमा है जो उस कोड के lock_guardविपरीत उपयोग करता है जो ए का उपयोग करता है unique_lock। आप जो पूछते हैं, उसे हासिल करने का एकमात्र तरीका है जानबूझकर lock_guardवर्ग के इनकैप्सुलेशन को तोड़ना और एक अलग वर्ग (इस मामले में condition_variable) के लिए इसके कार्यान्वयन को उजागर करना । यह दो लॉक प्रकारों के बीच अंतर को याद नहीं रखने के लिए एक चर के उपयोगकर्ता के संदिग्ध लाभ के लिए भुगतान करने के लिए एक कठिन कीमत है।
कॉमिकसंस

4
@ क्रिस आपको यह विचार कहां से मिला जो condition_variable_any.waitकाम करेगा lock_guard? मानक को आवश्यकता को पूरा करने के लिए प्रदान किए गए लॉक प्रकार की BasicLockableआवश्यकता होती है (.230.5.2), जो lock_guardनहीं करता है। केवल इसका अंतर्निहित म्यूटेक्स करता है, लेकिन जिन कारणों से मैंने पहले बताया था कि इंटरफ़ेस lock_guardम्यूटेक्स तक पहुंच प्रदान नहीं करता है।
कॉमिकसंस

11

वहाँ के बीच कुछ सामान्य बातें हैं lock_guardऔर unique_lockऔर कुछ मतभेद।

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

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

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


5
he compiler does not allow using a lock_guard in combination with a condition variableयह गलत है। यह निश्चित रूप से आईएनजी के साथ पूरी तरह से अनुमति देता है और काम करता है । केवल इंट पक्ष को एक की आवश्यकता है , क्योंकि स्थिति की जांच करते समय लॉक को जारी करना होगा। lock_guardnotify()wait()unique_lockwait()
अंडरस्कोर_ड

0

वे वास्तव में एक ही म्यूटेक्स नहीं हैं, lock_guard<muType>लगभग एक ही है std::mutex, एक अंतर के साथ यह है कि यह जीवनकाल गुंजाइश के अंत में समाप्त हो जाता है (डी-टोर कहा जाता है) इसलिए इन दो म्यूटेक्स के बारे में एक स्पष्ट परिभाषा:

lock_guard<muType> स्कूप्ड ब्लॉक की अवधि के लिए म्यूटेक्स के मालिक के लिए एक तंत्र है।

तथा

unique_lock<muType> एक रैपर है जो आस्थगित लॉकिंग की अनुमति देता है, लॉकिंग, पुनरावर्ती लॉकिंग, लॉक स्वामित्व के हस्तांतरण और स्थिति चर के साथ उपयोग में समय-विवश प्रयास।

यहाँ एक उदाहरण अनुकरण है:

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <chrono>

using namespace std::chrono;

class Product{

   public:

       Product(int data):mdata(data){
       }

       virtual~Product(){
       }

       bool isReady(){
       return flag;
       }

       void showData(){

        std::cout<<mdata<<std::endl;
       }

       void read(){

         std::this_thread::sleep_for(milliseconds(2000));

         std::lock_guard<std::mutex> guard(mmutex);

         flag = true;

         std::cout<<"Data is ready"<<std::endl;

         cvar.notify_one();

       }

       void task(){

       std::unique_lock<std::mutex> lock(mmutex);

       cvar.wait(lock, [&, this]() mutable throw() -> bool{ return this->isReady(); });

       mdata+=1;

       }

   protected:

    std::condition_variable cvar;
    std::mutex mmutex;
    int mdata;
    bool flag = false;

};

int main(){

     int a = 0;
     Product product(a);

     std::thread reading(product.read, &product);
     std::thread setting(product.task, &product);

     reading.join();
     setting.join();


     product.showData();
    return 0;
}

इस उदाहरण में, मैं के unique_lock<muType>साथ प्रयोग कियाcondition variable


-5

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

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

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

  • std :: condition_variable :: प्रतीक्षा (std :: unique_lock &) बस कॉल करता है pthread_cond_wait () अंतर्निहित pthread कंडीशन वैरिएबल पर mutex के संबंध में unique_lock द्वारा आयोजित (और इसलिए समान रूप से लॉक_गार्ड के लिए भी ऐसा ही हो सकता है, लेकिन मानक नहीं होने के कारण उस के लिए प्रदान नहीं करता है)
  • std :: condition_variable_any किसी भी लॉक करने योग्य ऑब्जेक्ट के साथ काम कर सकता है, जिसमें एक म्यूटेक्स लॉक भी नहीं है (यह एक अंतर-प्रक्रिया सेमाफोर के साथ भी काम कर सकता है)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.