সি ++ এ চলমান ধরণের মুটেক্সিসের সাথে কীভাবে व्यवहार করব?


86

ডিজাইন দ্বারা, std::mutexচলমান বা অনুলিপিযোগ্য নয়। এর অর্থ Aহ'ল মুটেেক্সযুক্ত একটি শ্রেণি কোনও ডিফল্ট মুভ কনস্ট্রাক্টর গ্রহণ করবে না।

আমি এই Aধরণেরটিকে থ্রেড-নিরাপদ উপায়ে কীভাবে অচল করতে পারি ?


4
প্রশ্নটির একটি তাত্ক্ষণিক প্রশ্ন রয়েছে: মুভ অপারেশনটি নিজেই থ্রেড-নিরাপদ হবে কি না, যদি বস্তুর অন্যান্য অ্যাক্সেসগুলি থ্রেড নিরাপদ থাকে তবে তা কি যথেষ্ট?
জোনাস শোফার

4
@ পলম এটি ডিজাইনের উপর নির্ভর করে। আমি প্রায়শই দেখেছি কোন ক্লাসে একটি মিটেক্স সদস্যের পরিবর্তনশীল থাকে, তবে কেবলমাত্র std::lock_guardপদ্ধতিটিই স্কোপড।
Cory Kramer

4
@ জোনাস উইলিকি: প্রথমে আমি ভেবেছিলাম এটি স্থানান্তরিত হওয়াও থ্রেড-নিরাপদ হওয়া উচিত। যাইহোক, আমি এটি নিয়ে আবারও ভাবি তা নয়, এটি কোনও তাত্পর্যপূর্ণ নয়, যেহেতু কোনও বস্তু সরানো-তৈরি করা সাধারণত পুরানো-অবজেক্টের অবস্থাকে অবৈধ করে দেয়। সুতরাং অন্য থ্রেডগুলি অবশ্যই পুরানো-অবজেক্টে অ্যাক্সেস করতে সক্ষম হবে না , যদি এটি সরানো হয় .. অন্যথায় তারা শীঘ্রই একটি অবৈধ বস্তু অ্যাক্সেস করতে পারে। আমি কি সঠিক?
জ্যাক সাবাথ

4
দয়া করে এই লিঙ্কটি সম্পূর্ণরূপে ব্যবহার করতে পারেন তা পুরোপুরি ব্যবহার করতে পারেন justtsoftwaresolutions.co.uk/threading/…
রবি চৌহান

4
@ ডিটার ল্যাকিং: হ্যাঁ, এটিই ধারণা .. এম এবং বি উভয়ই ক্লাস এ পড়তে পারে .. এবং এই ক্ষেত্রে ক্লাস এ এর ​​ক্লাসের সুযোগে একটি বিদ্রূপ থাকবে।
জ্যাক সাবাথ

উত্তর:


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:
    ...

আমি সেখানে কিছু পরিবর্তে পরামর্শমূলক ধরণের এলিয়াস রেখেছি যে আমরা সত্যই সি ++ 11 তে সুবিধা নেব না, তবে সি ++ 14 এ আরও দরকারী হয়ে উঠব। ধৈর্য ধরুন, আমরা সেখানে পৌঁছে যাব।

আপনার প্রশ্নটি এখানে ফোটে:

এই ক্লাসের জন্য আমি কীভাবে মুভ কনস্ট্রাক্টর এবং মুভ এসাইনমেন্ট অপারেটর লিখব?

আমরা মুভ কনস্ট্রাক্টর দিয়ে শুরু করব।

কনস্ট্রাক্টর সরান

নোট করুন যে সদস্যটি mutexকরা হয়েছে mutable। সরানো সদস্যদের জন্য কড়া কথা বলতে এটি প্রয়োজনীয় নয়, তবে আমি ধরে নিচ্ছি আপনিও অনুলিপি সদস্যদের চান want যদি এটি না হয় তবে মিউটেক্স তৈরি করার দরকার নেই 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 বা rh হয় অ্যাক্সেস করছে কিনা। এবং সাধারণভাবে, আপনাকে নিম্নলিখিত পরিস্থিতিতে রক্ষা করতে হবে:

// 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> এবং তাই এটি সত্যিকার অর্থে কোনও পার্থক্য করে না।

তবে সি ++ এ, আপনার এই কথাটি বলার বিকল্প থাকবে:

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

এটি একটি অপ্টিমাইজেশন হতে পারে, তবে অবশ্যই না। এটি কিনা তা নির্ধারণ করতে আপনাকে পরিমাপ করতে হবে। তবে এই পরিবর্তনের সাথে সাথে একসাথে একাধিক থ্রেডে একই আরএইচএস থেকে কন্ট্রাক্ট অনুলিপি করা যায় । সি ++ 11 সমাধান আপনাকে এই ধরনের থ্রেডগুলিকে অনুক্রমিক করে তুলতে বাধ্য করে, যদিও আরএইচএস পরিবর্তন করা হচ্ছে না।

অ্যাসাইনমেন্ট কপি করুন

সম্পূর্ণতার জন্য, এখানে অনুলিপি অ্যাসাইনমেন্ট অপারেটর, যা অন্য সমস্ত কিছু পড়ার পরে মোটামুটি স্ব-বর্ণনামূলক হওয়া উচিত:

    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

হালনাগাদ

ইয়াক্ক নীচের মন্তব্যে অনুলিপিটি কপির এবং মুভ কনস্ট্রাক্টরগুলিতে ডিফল্ট নির্মাণের বিষয়ে অসন্তুষ্ট (এবং তার একটি বক্তব্য রয়েছে)। আপনি যদি এই সমস্যাটি সম্পর্কে দৃ strongly়ভাবে অনুভব করেন, যাতে আপনি এটির উপর মেমরি ব্যয় করতে ইচ্ছুক হন, আপনি এটিকে এড়াতে পারেন:

  • ডেটা সদস্য হিসাবে আপনার প্রয়োজন মতো লক টাইপ যুক্ত করুন। এই সদস্যদের অবশ্যই সুরক্ষিত থাকা ডেটার আগে আসতে হবে:

    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();
    }
    

ওফস, ইয়াক্ক আমার এই আপডেটটি সম্পূর্ণ করার সুযোগ পাওয়ার আগে তার মন্তব্য মুছে ফেলল। তবে এই ইস্যুটি চাপ দেওয়ার এবং এই উত্তরের সমাধান পাওয়ার জন্য তিনি creditণ পাওয়ার যোগ্য।

আপডেট 2

এবং ডাইপ এই ভাল পরামর্শ নিয়ে আসে:

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

4
আপনার অনুলিপি নির্মাণকারী ক্ষেত্রগুলি বরাদ্দ করে, এটি তাদের অনুলিপি করে না। তার মানে তাদের ডিফল্ট-গঠনযোগ্য হতে হবে যা দুর্ভাগ্যজনক সীমাবদ্ধ।
ইয়াক্ক - অ্যাডাম নেভ্রামামন্ট

@ ইয়াক্ক: হ্যাঁ, mutexesশ্রেণির ধরণগুলি স্থাপন করা "একটি সত্য উপায়" নয়। এটি সরঞ্জামবাক্সের একটি সরঞ্জাম এবং আপনি যদি এটি ব্যবহার করতে চান তবে এটি।
হাওয়ার্ড হিন্যান্ট

@ ইয়াক্ক: "সি ++ ১৪" স্ট্রিংয়ের জন্য আমার উত্তরটি অনুসন্ধান করুন।
হাওয়ার্ড হিন্যান্ট

আহা, দুঃখিত, আমি C ++ 14 বিট মিস করেছি।
ইয়াক্ক - অ্যাডাম নেভ্রামুমন্ট

4
@ হওয়ার্ড হিন্যান্টের দুর্দান্ত ব্যাখ্যা সি ++ 17 এ আপনি স্টাডি :: স্কোপড_লক লক (x.mut_, y_mut_) ব্যবহার করতে পারেন; এই ভাবে আপনি বাস্তবায়ন নির্ভর একটি সঠিক অনুক্রমে বিভিন্ন mutexes লক করতে
Fen

7

এর জবাব দেওয়ার মতো সুন্দর, পরিষ্কার, সহজ উপায় বলে মনে হচ্ছে না - আন্তোনের সমাধান আমি মনে করি সঠিক, তবে এটির তাত্পর্যপূর্ণভাবে বিতর্কযোগ্য, যদি এর চেয়ে ভাল উত্তর না আসে তবে আমি এই ধরণের ক্লাসটি স্তূপের উপরে রাখার এবং এটির যত্ন নেওয়ার পরামর্শ দিই এর মাধ্যমে std::unique_ptr:

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

এটি এখন পুরোপুরি চলমান টাইপ এবং অভ্যন্তরীণ মিউটেক্সের সাথে তালুতে থাকা যে কেউ পদক্ষেপ নেওয়ার পরেও সে নিরাপদ, এমনকি এটি বিতর্কযোগ্য হলেও এটি করা ভাল যে এটি করা ভাল?

আপনার যদি কপির শব্দার্থবিদ্যা প্রয়োজন কেবল ব্যবহার করুন just

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)};
}

সি ++ 14 এবং সি ++ 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সেই ক্ষেত্রে জমিদার conficts সঙ্গে মোকাবিলা করার জন্য একটি ইনলাইন বন্ধু হবার শেষ হয়ে এসেছে।

সরাসরি উদাহরণ


4

মুডেক্সেস এবং সি ++ মুভ শব্দার্থক ব্যবহার থ্রেডগুলির মধ্যে ডেটা নিরাপদে এবং দক্ষতার সাথে স্থানান্তর করার একটি দুর্দান্ত উপায়।

এমন একটি 'প্রযোজক' থ্রেডের কল্পনা করুন যা স্ট্রিংগুলির ব্যাচ তৈরি করে এবং তাদের (এক বা একাধিক) গ্রাহকদের সরবরাহ করে। এই ব্যাচগুলি (সম্ভাব্য বৃহত) std::vector<std::string>অবজেক্টযুক্ত কোনও বস্তু দ্বারা প্রতিনিধিত্ব করা যেতে পারে । আমরা সম্পূর্ণরূপে অপ্রয়োজনীয় সদৃশ ছাড়াই ve ভেক্টরগুলির অভ্যন্তরীণ অবস্থাকে তাদের গ্রাহকদের মধ্যে স্থানান্তরিত করতে চাই।

আপনি কেবলমাত্র মুউটেক্সকে অবজেক্টের অংশ হিসাবে নয়, বস্তুর অংশ হিসাবে চিনতে পারেন। অর্থাত্ আপনি মুটেক্সটি সরাতে চান না।

আপনাকে কী লক করা দরকার তা নির্ভর করে আপনার অ্যালগরিদম বা কীভাবে আপনার অবজেক্টগুলি সাধারণীকরণ করা হয় এবং আপনি কত ধরণের ব্যবহারের অনুমতি দেন।

যদি আপনি কেবল কোনও ভাগ করা রাজ্য 'প্রযোজক' অবজেক্ট থেকে কোনও থ্রেড-লোকাল 'গ্রাহক' অবজেক্টে চলে যান তবে আপনি কেবলমাত্র অবজেক্ট থেকে স্থানান্তরিত লক করা ঠিক আছে ।

যদি এটি আরও সাধারণ নকশা হয় তবে আপনার উভয়কেই লক করতে হবে। এই জাতীয় ক্ষেত্রে আপনাকে তখন ডেড-লকিং বিবেচনা করা উচিত।

যদি এটি কোনও সম্ভাব্য সমস্যা হয় তবে std::lock()একটি অচলাবস্থামুক্ত পদ্ধতিতে উভয় মিটেক্সে লক সংগ্রহ করতে ব্যবহার করুন।

http://en.cppreferences.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.
    {
    }
};

এটি থ্রেড-সেফ, কারণ মুভ কনস্ট্রাক্টর নিরাপদে ধরে নিতে পারে যে এর যুক্তি অন্য কোথাও ব্যবহার করা হয়নি, সুতরাং আর্গুমেন্টের লকিংয়ের প্রয়োজন নেই।


4
এটি থ্রেড নিরাপদ নয়। a.mutexলক থাকলে কী হবে : আপনি সেই অবস্থাটি looseিলা করুন loose -1

4
@ ডিয়েটারলুকিং যতক্ষণ না আর্গুমেন্ট স্থানান্তরিত বস্তুর একমাত্র রেফারেন্স, ততক্ষণ তার ম্যুটেক্স লক হওয়ার কোনও বুদ্ধিমান কারণ নেই। এবং তা থাকলেও, সদ্য তৈরি হওয়া অবজেক্টের মিটেক্স লক করার কোনও কারণ নেই। এবং যদি থাকে তবে এটি মুটিেক্সেস সহ অস্থাবর বস্তুর সামগ্রিক খারাপ ডিজাইনের পক্ষে যুক্তি।
অ্যান্টন সাভিন

4
@ ডিয়েটারল্যাকিং এটি ঠিক সত্য নয়। আপনি কি সমস্যার একটি কোড সরবরাহ করতে পারেন? আর ফর্মে নয় A a; A a2(std::move(a)); do some stuff with a
আন্তন সাভিন

4
যাইহোক, যদি এটিই সর্বোত্তম উপায় ছিল তবে আমি যেভাবেই newউদাহরণটি আপ করার এবং এটিতে রাখার প্রস্তাব দিই std::unique_ptr- এটি পরিষ্কার বলে মনে হচ্ছে এবং বিভ্রান্তির কারণ হওয়ার সম্ভাবনা নেই। ভাল প্রশ্ন.
মাইক ভাইন

4
@ মাইকভাইন আমার মনে হয় আপনার উত্তর হিসাবে এটি যুক্ত করা উচিত।
আন্তন সাভিন
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.