std :: unique_lock <std :: mutex>またはstd :: lock_guard <std :: mutex>?


348

2つの使用例があります。

A. 2つのスレッドによるアクセスをキューに同期させたい。

B. 2つのスレッドによるキューへのアクセスを同期し、条件変数を使用したいのは、一方のスレッドが、もう一方のスレッドによってコンテンツがキューに格納されるのを待つためです。

ユースケースAIについては、を使用しstd::lock_guard<>たコード例を参照してください。使用例については、BIを使用したコード例を参照してくださいstd::unique_lock<>

この2つの違いと、どのユースケースでどちらを使用すればよいのですか?

回答:


344

違いは、をロックおよびロック解除できることですstd::unique_lockstd::lock_guard建設時に一度だけロックされ、破壊時にロック解除されます。

したがって、ユースケースBの場合std::unique_lock、条件変数には必ずa が必要です。ケースAでは、ガードを再度ロックする必要があるかどうかによって異なります。

std::unique_lockたとえば、ミューテックスをすぐにロックせずに構築し、RAIIラッパーを構築することを可能にする他の機能があります(ここを参照)。

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_guardstd::unique_lock同じです。したがって、上記の場合には、あなたは置き換えることができstd::lock_guardstd::unique_lock。ただし、std::unique_lockオーバーヘッドが少し増える可能性があります。

最近では、std::scoped_lockではなくを使用する必要があることに注意してくださいstd::lock_guard


2
命令std :: unique_lock <std :: mutex> lock(myMutex);を使用します。mutexはコンストラクターによってロックされますか?
chmike 2013

3
@chmikeはい、そうです。いくつかの説明を追加しました。
Stephan Dollberg

10
@chmikeええと、機能よりも効率の問題ではないと思います。std::lock_guardケースAで十分な場合は、それを使用する必要があります。不要なオーバーヘッドを回避するだけでなく、このガードのロックを解除しないことを読者に示します。
Stephan Dollberg

5
@chmike:理論的にはそうです。ただし、Muticesは厳密に軽量な構造ではないため、unique_lockmutexを実際にロックおよびロック解除するコストによって、追加のオーバーヘッドが小さくなります(コンパイラーがオーバーヘッドを最適化しなかった場合、可能性があります)。
Grizzly、

6
So for usecase B you definitely need a std::unique_lock for the condition variable-はい、ただしcv.wait() s のスレッドでのみです。そのメソッドは、ミューテックスをアトミックに解放するためです。あなたが共有変数(複数可)を更新し、他のスレッドで、その後呼び出しcv.notify_one()、シンプルlock_guardでスコープmutexをロックすればよいあなたは何をやっている場合を除き...もっと私は想像することができないという手の込んだ!例en.cppreference.com/w/cpp/thread/condition_variable-私のために働く:)
underscore_d

114

lock_guardunique_lockほとんど同じです。lock_guardインターフェースが制限された制限付きバージョンです。

Aはlock_guard常に、その構築から破壊までロックを保持しています。はunique_lockすぐにロックせずに作成でき、その存在の任意の時点でロック解除でき、ロックの所有権をインスタンス間で転送できます。

したがってlock_guard、の機能が必要でない限り、常にを使用しますunique_lock。Aにcondition_variableはが必要unique_lockです。


11
A condition_variable needs a unique_lock.-はい、ただしwait() infへの私のコメントで詳しく述べたように、ing側のみ。
underscore_d

48

を破壊せずlock_guardに手動unlockでミューテックスを手動で実行する必要がある場合を除き、使用しますlock

特に、のcondition_variable呼び出し時にスリープ状態になるときに、そのミューテックスをロック解除しwaitます。そのため、lock_guardここではa では不十分です。


何らかの理由で待機が終了すると常にミューテックスが再取得されるため、条件変数の待機メソッドの1つにlock_guardを渡しても問題ありません。ただし、この規格はunique_lockのインターフェースのみを提供します。これは規格の欠陥と見なすことができます。
Chris Vine、

3
@Chrisこの場合でも、カプセル化を解除します。waitメソッドは、からミューテックスを抽出してlock_guardロック解除できるようにする必要があるため、ガードのクラス不変式が一時的に解除されます。これはユーザーには見えませんが、lock_guardこのケースでの使用を許可しない正当な理由だと思います。
ComicSansMS 2013

もしそうなら、それは目に見えず、検出されません。gcc-4.8はそれを行います。wait(unique_lock <mutex>&)は__gthread_cond_wait(&_ M_cond、__lock.mutex()-> native_handle())を呼び出します(libstdc ++-v3 / src / c ++ 11 / condition_variable.ccを参照)。これはpthread_cond_wait()を呼び出します(libgccを参照) /gthr-posix.h)。同じことは、lock_guardに対しても行うことができます(ただし、condition_variableの標準にはないためです)。
Chris Vine、

4
@Chrisポイントはlock_guard、基礎となるミューテックスをまったく取得できないことです。これは、を使用lock_guardするコードとは対照的に、を使用するコードをより簡単に推論できるようにするための意図的な制限unique_lockです。要求することを達成する唯一の方法は、lock_guardクラスのカプセル化を意図的に解除し、その実装を別のクラス(この場合はcondition_variable)に公開することです。これは、2つのロックタイプの違いを覚えておく必要がない条件変数のユーザーの疑わしい利点に対して支払うのが難しい価格です。
ComicSansMS 2013

4
@クリスどこで使えるアイデアをどこで手に入れcondition_variable_any.waitたのlock_guard?標準では、提供されているロックタイプがBasicLockable要件(§30.5.2)を満たすことを要求lock_guardしていますが、これは満たしていません。基礎となるミューテックスのみが機能しますが、先に指摘した理由により、のインターフェースはlock_guardミューテックスへのアクセスを提供しません。
ComicSansMS 2013

11

間には一定の共通のものがあるlock_guardunique_lock、特定の違いとは。

しかし、質問の文脈ではlock_guard、スレッドが条件変数で待機を呼び出すと、ミューテックスが自動的にロック解除され、他のスレッド/スレッドが通知して現在のスレッドになると、コンパイラーは条件変数と組み合わせて使用することを許可しません(待機状態から抜ける)が呼び出されると、ロックが再取得されます。

この現象はの原則に反していlock_guardます。lock_guard構築できるのは1回だけで、破棄できるのは1回だけです。

したがってlock_guard、条件変数と組み合わせて使用​​することはできませんが、a unique_lockは使用できます(unique_lock複数回ロックおよびロック解除できるため)。


5
he compiler does not allow using a lock_guard in combination with a condition variableこれは誤りです。それは確かにないことを可能として完璧に仕事lock_guard上のnotify()INGの側。条件をチェックしている間ロックを解放wait()する必要があるunique_lockため、int側のみがを必要wait()とします。
underscore_d

0

それらは実際には同じmutexではなくlock_guard<muType>、とほぼ同じですがstd::mutex、存続期間がスコープの最後で終了する(D-torが呼び出される)点が異なるため、これらの2つの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待機関数がlock_guardだけでなく、unique_lockも使用してはならない理由はないようです。待機が終了すると(何らかの理由で)ミューテックスが自動的に再取得されるため、セマンティック違反は発生しません。ただし、標準に従って、条件変数でstd :: lock_guardを使用するには、std :: condition_variableの代わりにstd :: condition_variable_anyを使用する必要があります。

編集:「pthreadsインターフェースの使用std :: condition_variableとstd :: condition_variable_anyは同一でなければなりません」を削除しました。gccの実装を見ると:

  • std :: condition_variable :: wait(std :: unique_lock&)は、unique_lockが保持するミューテックスに関して、基礎となるpthread条件変数でpthread_cond_wait()を呼び出すだけです(したがって、lock_guardに対しても同じように同じことができますが、標準それを提供しません)
  • std :: condition_variable_anyは、mutexロックではないオブジェクトを含む、あらゆるロック可能なオブジェクトで機能します(したがって、プロセス間セマフォでも機能します)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.