2012-10-27 47 views
62

std::condition_variableで作業するとき、私はstd::unique_lockの役割について少し混乱しています。私が理解している限り、、std::unique_lockは基本的に肥大したロックガードであり、2つのロックの間で状態を交換する可能性があります。C++ 11:なぜstd :: condition_variableはstd :: unique_lockを使用しますか?

私はこれまでのところ(私はSTLがPOSIXに使用するものだと思う)、この目的のためにpthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)を使用しました。それはロックではなくミューテックスをとる。

ここでの違いは何ですか? はstd::unique_lockの最適化を処理するという事実はありますか?もしそうなら、それはどのくらい正確ですか?

+2

条件変数でロック/ミューテックスが必要な理由、またはロックとミューテックスの違い、または条件変数がミューテックスではなくユニークなロックを使用する理由について混乱していますか? – Brady

+0

"なぜ条件変数はmutexではなく一意のロックを使用するのですか?" –

答えて

91

ので、技術的な理由はありませんか?

私は彼が技術的理由を与えたと信じているので、cmeerwの答えをアップアップしました。それを歩みましょう。委員会がcondition_variablemutexに待つことを決めたとしよう。ここでのコードは、そのデザインを使用している:

void foo() 
{ 
    mut.lock(); 
    // mut locked by this thread here 
    while (not_ready) 
     cv.wait(mut); 
    // mut locked by this thread here 
    mut.unlock(); 
} 

これは、1つのcondition_variableを使うべきではありません正確にどのようにあります。

// mut locked by this thread here 

の領域では、例外的な安全上の問題があり、深刻な問題です。これらの領域(またはcv.wait自身)によって例外がスローされた場合、try/catchも例外を捕捉してロックを解除しない限り、mutexのロック状態がリークします。しかし、これはプログラマに書かせてほしいコードです。

プログラマが例外セーフコードを書く方法を知っていて、それを達成するためにunique_lockを使用することがわかっているとします。コードは次のようになります。

void foo() 
{ 
    unique_lock<mutex> lk(mut); 
    // mut locked by this thread here 
    while (not_ready) 
     cv.wait(*lk.mutex()); 
    // mut locked by this thread here 
} 

これははるかに優れていますが、まだまだ大きな状況ではありません。 condition_variableインターフェイスは、プログラマーが自分の道を離れて仕事をするようにしています。 lkが誤ってミューテックスを参照していない場合、ヌルポインタ逆参照が発生する可能性があります。そして、condition_variable::waitがこのスレッドがmutにロックを所有していることを確認する方法はありません。

ああ、ちょっと思い出して、プログラマが誤ってunique_lockメンバ関数を選択してミューテックスを公開する危険もあります。 はここで悲惨です。

今度は、コードがunique_lock<mutex>をとり、実際のcondition_variable APIで書かれている方法を見てみましょう:

void foo() 
{ 
    unique_lock<mutex> lk(mut); 
    // mut locked by this thread here 
    while (not_ready) 
     cv.wait(lk); 
    // mut locked by this thread here 
} 
  1. このコードは、それが得ることができるのと同じくらい簡単です。
  2. 例外的に安全です。
  3. wait関数はlk.owns_lock()をチェックし、falseの場合は例外をスローすることができます。

これは、APIデザインcondition_variableの技術的な理由によるものです。私はlock_guard<mutex>破棄されるまで、このミューテックスのロックを所有して:lock_guard<mutex>は、あなたが言うかあるので

さらに、condition_variable::waitlock_guard<mutex>を負いません。しかし、condition_variable::waitに電話すると、暗黙的にmutexのロックが解除されます。したがって、そのアクションはlock_guardユースケース/ステートメントと矛盾します。

1は、容器に入れて、関数からロックを返すことができるように私たちは、とにかくunique_lockを必要に応じて、ロック/は例外安全な方法で非スコープのパターンでミューテックスのロックを解除するので、unique_lockcondition_variable::waitのための自然な選択でした。

更新

bamboonはそうここに、そのIコントラストcondition_variable_any以下のコメントで示唆:

質問:私はそれにあらゆるLockable型を渡すことができるようにテンプレートではないcondition_variable::waitれるのはなぜ

回答:

持っている本当にクールな機能です。たとえば、this paperは、条件変数(posix世界では聞こえていないものの、それでもなお非常に便利です)で共有モードでshared_lock(rwlock)を待つコードを示しています。しかし、その機能はより高価です。 あらゆるロック可能なタイプに待つことができます。このcondition_variableアダプタ 1で

`condition_variable_any` 

だから委員会は、この機能を持つ新しいタイプを導入しました。メンバーに lock()unlock()がある場合、あなたはいいです。 condition_variable_anyを適切に実装するには、データメンバー condition_variableとデータメンバー shared_ptr<mutex>が必要です。

この新しい機能は基本的なcondition_variable::waitよりも高価であり、condition_variableはそのような低レベルのツールなので、この非常に便利だが高価な機能は別のクラスに入れられているため、 。

+0

"それ"を明確にすることができますか?あなたは 'lock_guard'や' condition_variable'、あるいはおそらく 'condition_variable :: wait'について話していますか? –

+0

申し訳ありません、忘れてください。私は 'condition_variable_any'を完全に忘れていた – inf

+0

ああ、テンプレート' condition_variable :: wait'。はい、 'condition_variable_any'は行く方法です。そして、機能が 'condition_variable'に折り畳まれていない理由は、それがより高価であるということです。そして、 'condition_variable'はそのような低レベルのツールです。できるだけ効率的なものにする必要がありました。 'condition_variable_any'を使用すると、追加された機能に対してのみ支払いを行います。 –

32

それは本質的に(無視できると見られて、追加のオーバーヘッドが)デフォルトでは、可能なAPIが限り安全にするAPIの設計上の決定です。生の代わりにunique_lockを渡すことを要求することにより、APIのユーザーは(例外の存在下で)正しいコードを書くように指示されます。mutex

近年ではC++言語の焦点は、デフォルトでは、それが安全作る(それでも、彼らがしたいと十分に懸命しようとした場合、ユーザーは自分の足に自分自身を撮影することができます)の方にシフトしています。

+0

技術的な理由はありませんか? –

関連する問題