79

std::unique_lockで作業するときの役割について少し混乱していstd::condition_variableます。私がドキュメントを理解している限り、std::unique_lockは基本的に肥大化したロックガードであり、2つのロック間で状態を交換する可能性があります。

私はこれまでpthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)この目的で使用してきました(STLがposixで使用しているのはそれだと思います)。ロックではなく、ミューテックスが必要です。

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

4

2 に答える 2

109

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

彼が技術的な理由を与えたと私は信じているので、私はcmeerwの答えに賛成した。それを見ていきましょう。condition_variable委員会がを待つことにしたとしましょうmutex。その設計を使用したコードは次のとおりです。

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

これはまさに、を使用すべきではないcondition_variable方法です。でマークされた地域:

// mut locked by this thread here

例外安全性の問題があり、それは深刻な問題です。これらの領域で(または単独で)例外がスローされた場合、例外cv.waitをキャッチしてロックを解除するためにtry / catchもどこかに配置されない限り、ミューテックスのロック状態がリークされます。しかし、それはあなたがプログラマーに書くように頼んでいる単なるより多くのコードです。

プログラマーが例外安全なコードを書く方法を知っていて、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誤ってミューテックスを参照しない場合、nullポインタの逆参照が発生する可能性があります。condition_variable::waitそして、このスレッドがのロックを所有していることを確認する方法はありませんmut

ああ、覚えておいてください、プログラマーがunique_lockミューテックスを公開するために間違ったメンバー関数を選択するかもしれないという危険もあります。 *lk.release()ここでは悲惨なことになります。

次に、 :を使用する実際のcondition_variableAPIを使用してコードがどのように記述されているかを見てみましょう。unique_lock<mutex>

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

さらに、あなたが言うように理由はcondition_variable::waitありません:私は破壊されるまでこのミューテックスのロックを所有しています。ただし、を呼び出すと、ミューテックスのロックが暗黙的に解放されます。そのため、アクションはユースケース/ステートメントと矛盾します。lock_guard<mutex>lock_guard<mutex>lock_guard<mutex>condition_variable::waitlock_guard

unique_lockとにかく、関数からロックを返し、それらをコンテナーに入れ、例外安全な方法でスコープ外のパターンでミューテックスをロック/ロック解除できるようにする必要があったので、unique_lockの自然な選択でしたcondition_variable::wait

アップデート

バンボンは私が対照的であると以下のコメントで提案したcondition_variable_anyので、ここに行きます:

質問:任意のタイプをcondition_variable::wait渡すことができるようにテンプレート化されて いないのはなぜですか?Lockable

答え:

それは本当に素晴らしい機能です。たとえば、このペーパーshared_lockでは、条件変数(posixの世界では前例のないものですが、それでも非常に便利です)で共有モードの(rwlock)を待機するコードを示します。ただし、機能はより高価です。

そこで、委員会はこの機能を備えた新しいタイプを導入しました。

`condition_variable_any`

このcondition_variable アダプタを使用するとロック可能なタイプを待つことができます。メンバーがいる場合はlock()unlock()行ってもいいです。の適切な実装にcondition_variable_anyは、condition_variableデータメンバーとデータメンバーが必要shared_ptr<mutex>です。

Because this new functionality is more expensive than your basic condition_variable::wait, and because condition_variable is such a low level tool, this very useful but more expensive functionality was put into a separate class so that you only pay for it if you use it.

于 2012-10-27T18:21:00.813 に答える
36

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

近年、C ++言語の焦点は、デフォルトで安全にすることにシフトしています(ただし、ユーザーが必要に応じて自分の足を踏み入れて、十分に努力できるようにすることもできます)。

于 2012-10-27T14:18:47.617 に答える