मुझे C ++ में चल प्रकार में म्यूटेक्स से कैसे निपटना चाहिए?


86

डिजाइन के अनुसार, std::mutexचल नहीं है और न ही प्रतिलिपि बनाई जा सकती है। इसका अर्थ है कि Aम्यूटेक्स रखने वाले वर्ग को डिफ़ॉल्ट मूव कंस्ट्रक्टर प्राप्त नहीं होगा।

मैं इस प्रकार Aको एक थ्रेड-सुरक्षित तरीके से कैसे चल सकता हूं ?


4
प्रश्न में एक विचित्रता है: चाल ऑपरेशन स्वयं भी थ्रेड-सुरक्षित होगा, या क्या यह पर्याप्त है यदि ऑब्जेक्ट तक अन्य पहुंच सुरक्षित है?
जोनास शोफर

2
@ पुल्म जो वास्तव में डिजाइन पर निर्भर करता है। मैंने अक्सर देखा है कि एक वर्ग में एक म्यूटेक्स सदस्य चर होता है, फिर केवल std::lock_guardविधि को स्कोप किया जाता है।
कोरी क्रेमर

2
@ जोनास विल्की: पहले मुझे लगा कि इसे स्थानांतरित करना भी थ्रेड-सुरक्षित होना चाहिए। हालांकि, ऐसा नहीं है कि मैं इसके बारे में फिर से सोचता हूं, इसका कोई मतलब नहीं है, क्योंकि किसी ऑब्जेक्ट को स्थानांतरित करना आमतौर पर पुरानी वस्तु की स्थिति को अमान्य करता है। इसलिए अन्य थ्रेड पुरानी वस्तु तक नहीं पहुंच सकते हैं, अगर यह स्थानांतरित होने जा रहा है .. अन्यथा वे जल्द ही एक अमान्य वस्तु तक पहुंच सकते हैं। क्या मैं सही हू?
जैक सब्बाथ

2
कृपया इस लिंक का पालन करें, इसके लिए इसका पूरा उपयोग कर सकते हैं justsoftwaresolutions.co.uk/threading/…
रवि चौहान

1
@ डायर लुकिंग: हाँ, यह विचार है .. म्यूटेक्स एम कक्षा बी की सुरक्षा करता है। हालांकि, मैं दोनों को कहां सुरक्षित, सुलभ वस्तु के लिए स्टोर कर सकता हूं? M और B दोनों कक्षा A में जा सकते हैं .. और इस मामले में कक्षा A में वर्ग दायरे में Mutex होगा।
जैक सब्बाथ

जवाबों:


105

चलो कोड के एक बिट के साथ शुरू करते हैं:

class A
{
    using MutexType = std::mutex;
    using ReadLock = std::unique_lock<MutexType>;
    using WriteLock = std::unique_lock<MutexType>;

    mutable MutexType mut_;

    std::string field1_;
    std::string field2_;

public:
    ...

मैंने कुछ विचारोत्तेजक प्रकार के उपनामों को वहाँ रखा है कि हम वास्तव में C ++ 11 में लाभ नहीं लेंगे, लेकिन C ++ 14 में बहुत अधिक उपयोगी बन जाते हैं। धीरज रखो, हम वहाँ पहुँचेंगे।

आपका प्रश्न निम्नलिखित पर उबलता है:

मैं इस वर्ग के लिए मूव कंस्ट्रक्टर और मूव असाइनमेंट ऑपरेटर कैसे लिखूँ?

हम कदम निर्माता के साथ शुरू करेंगे।

कंस्ट्रक्टर को मूव करें

ध्यान दें कि सदस्य mutexबनाया गया है mutable। सख्ती से बोलना इस कदम के सदस्यों के लिए आवश्यक नहीं है, लेकिन मुझे लगता है कि आप भी कॉपी सदस्यों को चाहते हैं। यदि ऐसा नहीं है, तो म्यूटेक्स बनाने की कोई आवश्यकता नहीं है mutable

निर्माण करते समय A, आपको लॉक करने की आवश्यकता नहीं है this->mut_। लेकिन आपको mut_उस ऑब्जेक्ट को लॉक करने की आवश्यकता है जिसे आप (स्थानांतरित या प्रतिलिपि) से निर्माण कर रहे हैं। यह इस तरह किया जा सकता है:

    A(A&& a)
    {
        WriteLock rhs_lk(a.mut_);
        field1_ = std::move(a.field1_);
        field2_ = std::move(a.field2_);
    }

ध्यान दें कि हमें पहले के सदस्यों का निर्माण डिफ़ॉल्ट रूप से करना था this, और फिर a.mut_लॉक होने के बाद ही उन्हें मान असाइन करें ।

असाइनमेंट ले जाएं

चाल असाइनमेंट ऑपरेटर काफी अधिक जटिल है क्योंकि आपको नहीं पता है कि कुछ अन्य थ्रेड असाइनमेंट अभिव्यक्ति के lhs या rs तक पहुँच रहे हैं या नहीं। और सामान्य तौर पर, आपको निम्नलिखित परिदृश्य की रक्षा करने की आवश्यकता है:

// Thread 1
x = std::move(y);

// Thread 2
y = std::move(x);

यहाँ चाल असाइनमेंट ऑपरेटर है जो उपरोक्त परिदृश्य को सही ढंग से रखता है:

    A& operator=(A&& a)
    {
        if (this != &a)
        {
            WriteLock lhs_lk(mut_, std::defer_lock);
            WriteLock rhs_lk(a.mut_, std::defer_lock);
            std::lock(lhs_lk, rhs_lk);
            field1_ = std::move(a.field1_);
            field2_ = std::move(a.field2_);
        }
        return *this;
    }

ध्यान दें कि एक के बाद एक std::lock(m1, m2)को लॉक करने के बजाय, दो म्यूटेक्स को लॉक करने के लिए उपयोग करना चाहिए । यदि आप उन्हें एक के बाद एक लॉक करते हैं, तो जब दो धागे विपरीत क्रम में दो वस्तुओं को असाइन करते हैं जैसा कि ऊपर दिखाया गया है, तो आप एक गतिरोध प्राप्त कर सकते हैं। की बात std::lockहै कि गतिरोध से बचना है।

कापी कंस्ट्रक्टर

आपने कॉपी सदस्यों के बारे में नहीं पूछा, लेकिन हम अब उनके बारे में बात कर सकते हैं (यदि आप नहीं, तो किसी को उनकी आवश्यकता होगी)।

    A(const A& a)
    {
        ReadLock  rhs_lk(a.mut_);
        field1_ = a.field1_;
        field2_ = a.field2_;
    }

कॉपी कंस्ट्रक्टर काफी हद तक मूव कंस्ट्रक्टर की तरह दिखता है, सिवाय इसके कि ReadLockउर्फ का उपयोग किया जाता है WriteLock। वर्तमान में इन दोनों उपनामों std::unique_lock<std::mutex>और इसलिए यह वास्तव में कोई फर्क नहीं पड़ता है।

लेकिन C ++ 14 में, आपके पास यह कहने का विकल्प होगा:

    using MutexType = std::shared_timed_mutex;
    using ReadLock  = std::shared_lock<MutexType>;
    using WriteLock = std::unique_lock<MutexType>;

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

असाइनमेंट कॉपी करें

पूर्णता के लिए, यहां कॉपी असाइनमेंट ऑपरेटर है, जिसे बाकी चीजों के बारे में पढ़ने के बाद काफी आत्म व्याख्यात्मक होना चाहिए:

    A& operator=(const A& a)
    {
        if (this != &a)
        {
            WriteLock lhs_lk(mut_, std::defer_lock);
            ReadLock  rhs_lk(a.mut_, std::defer_lock);
            std::lock(lhs_lk, rhs_lk);
            field1_ = a.field1_;
            field2_ = a.field2_;
        }
        return *this;
    }

और आदि।

Aयदि आप एक बार में कई थ्रेड्स को कॉल करने में सक्षम होने की अपेक्षा करते हैं, तो किसी भी अन्य सदस्य या नि: शुल्क फ़ंक्शन को भी एक्सेस करना होगा। उदाहरण के लिए, यहाँ है swap:

    friend void swap(A& x, A& y)
    {
        if (&x != &y)
        {
            WriteLock lhs_lk(x.mut_, std::defer_lock);
            WriteLock rhs_lk(y.mut_, std::defer_lock);
            std::lock(lhs_lk, rhs_lk);
            using std::swap;
            swap(x.field1_, y.field1_);
            swap(x.field2_, y.field2_);
        }
    }

ध्यान दें कि यदि आप सिर्फ std::swapकाम करने पर निर्भर हैं , तो लॉकिंग गलत ग्रैन्युलैरिटी पर std::swapहोगा, आंतरिक रूप से प्रदर्शन करने वाले तीन चालों के बीच लॉकिंग और अनलॉकिंग होगी।

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

"स्व-स्वैप" से बचाने की आवश्यकता पर भी ध्यान दें। "स्व-स्वैप" एक नो-ऑप होना चाहिए। स्व-जांच के बिना एक ही म्यूटेक्स को पुन: लॉक किया जाएगा। यह भी का उपयोग करके स्वयं की जांच के बिना हल किया जा सकता std::recursive_mutexके लिए MutexType

अपडेट करें

नीचे दिए गए टिप्पणियों में याक को कॉपी और मूव कन्स्ट्रक्टर्स (और उसके पास एक बिंदु) में चीजों को डिफ़ॉल्ट रूप से बनाने के बारे में बहुत दुखी है। क्या आपको इस मुद्दे के बारे में पर्याप्त रूप से महसूस करना चाहिए, इतना है कि आप इस पर स्मृति खर्च करने के लिए तैयार हैं, आप इसे इस तरह से बचा सकते हैं:

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

    mutable MutexType mut_;
    ReadLock  read_lock_;
    WriteLock write_lock_;
    // ... other data members ...
    
  • और फिर कंस्ट्रक्टर्स में (जैसे कॉपी कंस्ट्रक्टर) ऐसा करें:

    A(const A& a)
        : read_lock_(a.mut_)
        , field1_(a.field1_)
        , field2_(a.field2_)
    {
        read_lock_.unlock();
    }
    

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

अपडेट २

और इस अच्छे सुझाव के साथ dyp आया:

    A(const A& a)
        : A(a, ReadLock(a.mut_))
    {}
private:
    A(const A& a, ReadLock rhs_lk)
        : field1_(a.field1_)
        , field2_(a.field2_)
    {}

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

@ यक: हाँ, mutexesक्लास के प्रकारों में डालना "एक सही तरीका" नहीं है। यह टूलबॉक्स में एक उपकरण है और यदि आप इसका उपयोग करना चाहते हैं, तो यह है।
हावर्ड हिनान्ट

@Yakk: स्ट्रिंग "C ++ 14" के लिए मेरा उत्तर खोजें।
हावर्ड हिनान्ट

आह, माफ करना, मुझे याद है कि C ++ 14 बिट।
यक्क - एडम नेवरामोंट

2
महान स्पष्टीकरण @HowardHinnant! C ++ 17 में आप std :: scoped_lock lock (x.mut_, y_mut_) का भी उपयोग कर सकते हैं; इस तरह आप एक उचित क्रम में कई म्यूटेक्स को बंद करने के लिए कार्यान्वयन पर भरोसा करते हैं
fen

7

यह देखते हुए कि यह जवाब देने के लिए एक अच्छा, साफ, आसान तरीका नहीं है - मुझे लगता है कि एंटोन का समाधान सही है, लेकिन निश्चित रूप से बहस का मुद्दा है, जब तक कि बेहतर जवाब नहीं आता है, मैं इस तरह के वर्ग को ढेर पर रखने और इसकी देखभाल करने की सलाह दूंगा। एक के माध्यम से std::unique_ptr:

auto a = std::make_unique<A>();

यह अब पूरी तरह से चल प्रकार का है और कोई भी व्यक्ति जिसके पास आंतरिक म्यूटेक्स पर एक ताला है, जबकि एक चाल होती है वह अभी भी सुरक्षित है, भले ही इसकी बहस करने योग्य है कि क्या यह करना एक अच्छी बात है

यदि आपको कॉपी शब्दार्थों का उपयोग करने की आवश्यकता है

auto a2 = std::make_shared<A>();

5

यह उलटा जवाब है। टाइप के आधार के रूप में "इस ऑब्जेक्ट को सिंक्रनाइज़ करने की आवश्यकता है" को एम्बेड करने के बजाय, इसे किसी भी प्रकार के तहत इंजेक्ट करें।

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

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

यहाँ एक मनमाना प्रकार के आसपास एक सिंक्रनाइज़ रैपर है T:

template<class T>
struct synchronized {
  template<class F>
  auto read(F&& f) const&->std::result_of_t<F(T const&)> {
    return access(std::forward<F>(f), *this);
  }
  template<class F>
  auto read(F&& f) &&->std::result_of_t<F(T&&)> {
    return access(std::forward<F>(f), std::move(*this));
  }
  template<class F>
  auto write(F&& f)->std::result_of_t<F(T&)> {
    return access(std::forward<F>(f), *this);
  }
  // uses `const` ness of Syncs to determine access:
  template<class F, class... Syncs>
  friend auto access( F&& f, Syncs&&... syncs )->
  std::result_of_t< F(decltype(std::forward<Syncs>(syncs).t)...) >
  {
    return access2( std::index_sequence_for<Syncs...>{}, std::forward<F>(f), std::forward<Syncs>(syncs)... );
  };
  synchronized(synchronized const& o):t(o.read([](T const&o){return o;})){}
  synchronized(synchronized && o):t(std::move(o).read([](T&&o){return std::move(o);})){}  
  // special member functions:
  synchronized( T & o ):t(o) {}
  synchronized( T const& o ):t(o) {}
  synchronized( T && o ):t(std::move(o)) {}
  synchronized( T const&& o ):t(std::move(o)) {}
  synchronized& operator=(T const& o) {
    write([&](T& t){
      t=o;
    });
    return *this;
  }
  synchronized& operator=(T && o) {
    write([&](T& t){
      t=std::move(o);
    });
    return *this;
  }
private:
  template<class X, class S>
  static auto smart_lock(S const& s) {
    return std::shared_lock< std::shared_timed_mutex >(s.m, X{});
  }
  template<class X, class S>
  static auto smart_lock(S& s) {
    return std::unique_lock< std::shared_timed_mutex >(s.m, X{});
  }
  template<class L>
  static void lock(L& lockable) {
      lockable.lock();
  }
  template<class...Ls>
  static void lock(Ls&... lockable) {
      std::lock( lockable... );
  }
  template<size_t...Is, class F, class...Syncs>
  friend auto access2( std::index_sequence<Is...>, F&&f, Syncs&&...syncs)->
  std::result_of_t< F(decltype(std::forward<Syncs>(syncs).t)...) >
  {
    auto locks = std::make_tuple( smart_lock<std::defer_lock_t>(syncs)... );
    lock( std::get<Is>(locks)... );
    return std::forward<F>(f)(std::forward<Syncs>(syncs).t ...);
  }

  mutable std::shared_timed_mutex m;
  T t;
};
template<class T>
synchronized< T > sync( T&& t ) {
  return {std::forward<T>(t)};
}

C ++ 14 और C ++ 1z विशेषताएं शामिल हैं।

यह मानता है कि constऑपरेशन कई-पाठक सुरक्षित हैं (जो stdकंटेनर को मानते हैं)।

उपयोग इस तरह दिखता है:

synchronized<int> x = 7;
x.read([&](auto&& v){
  std::cout << v << '\n';
});

एक के लिए intसिंक्रनाइज़ का उपयोग के साथ।

मैं होने के खिलाफ सलाह देंगे synchronized(synchronized const&)। इसकी जरूरत शायद ही हो।

आप की जरूरत है synchronized(synchronized const&), मैं बदलने के लिए परीक्षा होगी T t;साथ std::aligned_storage, मैनुअल प्लेसमेंट निर्माण की इजाजत दी, और मैनुअल विनाश करते हैं। यह उचित जीवनकाल प्रबंधन की अनुमति देता है।

इसे छोड़कर, हम स्रोत की प्रतिलिपि बना सकते हैं T, फिर उससे पढ़ सकते हैं:

synchronized(synchronized const& o):
  t(o.read(
    [](T const&o){return o;})
  )
{}
synchronized(synchronized && o):
  t(std::move(o).read(
    [](T&&o){return std::move(o);})
  )
{}

असाइनमेंट के लिए:

synchronized& operator=(synchronized const& o) {
  access([](T& lhs, T const& rhs){
    lhs = rhs;
  }, *this, o);
  return *this;
}
synchronized& operator=(synchronized && o) {
  access([](T& lhs, T&& rhs){
    lhs = std::move(rhs);
  }, *this, std::move(o));
  return *this;
}
friend void swap(synchronized& lhs, synchronized& rhs) {
  access([](T& lhs, T& rhs){
    using std::swap;
    swap(lhs, rhs);
  }, *this, o);
}

प्लेसमेंट और संरेखित स्टोरेज संस्करण थोड़ा गड़बड़ है। अधिकांश पहुंच को tसदस्य फ़ंक्शन द्वारा प्रतिस्थापित किया जाएगा T&t()और T const&t()constनिर्माण को छोड़कर, जहां आपको कुछ हुप्स के माध्यम से कूदना होगा।

synchronizedकक्षा के हिस्से के बजाय एक आवरण बनाकर , हम सभी को यह सुनिश्चित करना होगा कि वर्ग आंतरिक constरूप से बहु-पाठक होने के नाते सम्मान करता है , और इसे एकल-सूत्र में लिखता है।

में दुर्लभ मामलों में हम एक तुल्यकालन उदाहरण की जरूरत है, हम ऊपर की तरह हुप्स के माध्यम से कूद।

उपरोक्त में किसी भी टाइपोस के लिए माफी। शायद कुछ हैं।

ऊपर एक साइड का लाभ यह है कि synchronizedवस्तुओं पर (समान प्रकार के) एन-एरी मनमाना संचालन एक साथ काम करते हैं, बिना हाथ से इसे हार्ड-कोड किए बिना। एक मित्र घोषणा में जोड़ें और synchronizedकई प्रकार की n-ary ऑब्जेक्ट एक साथ काम कर सकते हैं। मुझे accessउस मामले में अधिभार संघर्ष से निपटने के लिए एक इनलाइन मित्र होने से बाहर होना पड़ सकता है ।

जीवंत उदाहरण


4

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

एक 'निर्माता' थ्रेड की कल्पना करें जो स्ट्रिंग्स के बैच बनाता है और उन्हें (एक या अधिक) उपभोक्ताओं को प्रदान करता है। उन बैचों को एक ऑब्जेक्ट (संभावित बड़ी) std::vector<std::string>वस्तुओं द्वारा दर्शाया जा सकता है । हम पूरी तरह से उन वैक्टरों की आंतरिक स्थिति को अनावश्यक रूप से दोहराए बिना अपने उपभोक्ताओं में ले जाना चाहते हैं।

आप बस वस्तु के भाग के रूप में म्यूटेक्स को पहचानते हैं जो वस्तु की स्थिति का हिस्सा नहीं है। यही है, आप म्यूटेक्स को स्थानांतरित नहीं करना चाहते हैं।

आपको किस लॉकिंग की आवश्यकता है यह आपके एल्गोरिथ्म पर निर्भर करता है या आपकी वस्तुओं का सामान्यीकरण कैसे होता है और आप किस सीमा तक उपयोग करते हैं।

यदि आप केवल एक साझा राज्य 'निर्माता' ऑब्जेक्ट से थ्रेड-लोकल 'उपभोग' ऑब्जेक्ट पर ले जाते हैं, तो आप केवल ऑब्जेक्ट के लिए ले जाया गया लॉक करना ठीक हो सकता है ।

यदि यह अधिक सामान्य डिज़ाइन है तो आपको दोनों को लॉक करने की आवश्यकता होगी। ऐसे मामले में आपको मृत-लॉकिंग पर विचार करने की आवश्यकता है।

यदि यह एक संभावित मुद्दा है, तो std::lock()एक गतिरोध मुक्त तरीके से दोनों म्यूटेक्स पर ताले प्राप्त करने के लिए उपयोग करें।

http://en.cppreference.com/w/cpp/thread/lock

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

फिर से मेरा प्रोड्यूसर स्ट्रिंग्स को पीट रहा है और उपभोक्ता पूरा लोड निकाल रहा है। उस स्थिति में हर बार जब निर्माता वेक्टर को जोड़ने की कोशिश करता है तो उसे वेक्टर गैर-रिक्त या खाली मिल सकता है।

संक्षेप में अगर संभावित समवर्ती का उपयोग वस्तु राशियों से किसी लेखन में स्थानांतरित हो जाए तो यह ठीक है। यदि यह एक पढ़ने के लिए है, तो इसके बारे में सोचें कि मनमाना राज्य पढ़ने के लिए क्यों ठीक है।


3

सबसे पहले, आपके डिज़ाइन में कुछ गड़बड़ होनी चाहिए यदि आप किसी म्यूटेक्स युक्त ऑब्जेक्ट को स्थानांतरित करना चाहते हैं।

लेकिन अगर आप इसे वैसे भी करने का फैसला करते हैं, तो आपको मूव कंस्ट्रक्टर में एक नया म्यूटेक्स बनाना होगा, जैसे:

// movable
struct B{};

class A {
    B b;
    std::mutex m;
public:
    A(A&& a)
        : b(std::move(a.b))
        // m is default-initialized.
    {
    }
};

यह थ्रेड-सुरक्षित है, क्योंकि मूव कंस्ट्रक्टर सुरक्षित रूप से मान सकता है कि इसके तर्क का उपयोग कहीं और नहीं किया गया है, इसलिए लॉजिक को लॉक करने की आवश्यकता नहीं है।


2
वह धागा सुरक्षित नहीं है। क्या होगा अगर a.mutexबंद है,: आप उस राज्य को ढीला करते हैं। -1

2
@ DieterLücking जब तक यह तर्क स्थानांतरित-से-ऑब्जेक्ट का एकमात्र संदर्भ है, तब तक इसके म्यूटेक्स को लॉक किए जाने का कोई कारण नहीं है। और यहां तक ​​कि अगर यह है, तो एक नई बनाई गई वस्तु के म्यूटेक्स को लॉक करने का कोई कारण नहीं है। और अगर वहाँ है, तो यह म्यूटेक्स के साथ चल वस्तुओं के समग्र खराब डिजाइन के लिए एक तर्क है।
एंटोन सविन

1
@ DieterLücking यह सिर्फ सच नहीं है। क्या आप समस्या को दर्शाने वाला कोड प्रदान कर सकते हैं? और रूप में नहीं A a; A a2(std::move(a)); do some stuff with a
एंटोन सविन

2
हालांकि, अगर यह सबसे अच्छा तरीका था, तो मैं किसी भी तरह newसे उदाहरण की सिफारिश करूंगा और इसे ऐसे में रखूंगा std::unique_ptr- जो क्लीनर लगता है और भ्रम की स्थिति पैदा करने की संभावना नहीं है। अच्छा प्रश्न।
माइक वाइन

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