15

私はC++11の同時実行性について学んでいます。同時実行性プリミティブに関する私の唯一の以前の経験は、6年前のオペレーティングシステムクラスでした。できれば、穏やかにしてください。

C ++ 11では、次のように書くことができます

std::mutex m;
std::condition_variable cv;
std::queue<int> q;

void producer_thread() {
    std::unique_lock<std::mutex> lock(m);
    q.push(42);
    cv.notify_one();
}

void consumer_thread() {
    std::unique_lock<std::mutex> lock(m);
    while (q.empty()) {
        cv.wait(lock);
    }
    q.pop();
}

cv.waitこれは問題なく機能しますが、ループでラップする必要があることに腹を立てています。ループが必要な理由は私には明らかです。

Consumer (inside wait())       Producer            Vulture

release the lock
sleep until notified
                               acquire the lock
                               I MADE YOU A COOKIE
                               notify Consumer
                               release the lock
                                                   acquire the lock
                                                   NOM NOM NOM
                                                   release the lock
acquire the lock
return from wait()
HEY WHERE'S MY COOKIE                              I EATED IT

さて、私unique_lockはそれについてのクールなことの1つは、私たちがそれを回すことができるということだと思いますよね?したがって、代わりにこれを行うことができれば、本当にエレガントになります。

Consumer (inside wait())       Producer

release the lock
sleep until notified
                               acquire the lock
                               I MADE YOU A COOKIE
                               notify and yield(passing the lock)
wake(receiving the lock)
return from wait()
YUM
release the lock

ミューテックスはからI MADE YOU A COOKIEまでずっとロックされたままなので、Vultureスレッドが急降下する方法はありませんYUM。さらに、notify()ロックを渡す必要がある場合は、呼び出す前に実際にミューテックスをロックすることを確認するのに適した方法です(条件変数(pthread)のシグナリングnotify()を参照)。

C++11にはこのイディオムの標準的な実装がないことは間違いありません。その歴史的な理由は何ですか(pthreadがそれをしなかったというだけですか?そしてそれはなぜですか)?冒険的なC++コーダーがこのイディオムを標準のC++11で実装できなかったという技術的な理由はありmy_better_condition_variableますか?

また、セマフォを作り直しているのではないかと漠然と感じていますが、それが正しいかどうかを学校で十分に覚えていません。

4

4 に答える 4

10

最終的な答えは、pthreadがそれを行わなかったためです。C ++は、オペレーティングシステムの機能をカプセル化する言語です。C++はオペレーティングシステムまたはプラットフォームではありません。そのため、Linux、UNIX、Windowsなどのオペレーティングシステムの既存の機能をカプセル化します。

ただし、pthreadには、この動作についても十分な根拠があります。Open Groupの基本仕様から:

その結果、pthread_cond_signal()を1回呼び出すと、複数のスレッドがpthread_cond_wait()またはpthread_cond_timedwait()を呼び出すと戻る可能性があります。この効果は「スプリアスウェイクアップ」と呼ばれます。そのように目覚めたスレッドの数が有限であるという点で、状況は自己修正的であることに注意してください。たとえば、ブロックの上の一連のイベントの後にpthread_cond_wait()を呼び出す次のスレッド。

この問題は解決できますが、特に条件変数に関連付けられた述語をチェックする必要があることを考えると、めったに発生しないフリンジ条件の効率の低下は許容できません。この問題を修正すると、すべての高レベルの同期操作について、この基本的な構成要素の並行性の程度が不必要に低下します。

スプリアスウェイクアップを許可することの追加の利点は、アプリケーションが条件待機の周りに述語テストループをコーディングすることを余儀なくされることです。これにより、アプリケーションは、アプリケーションの他の部分でコーディングされる可能性のある同じ条件変数での余分な条件のブロードキャストまたは信号を許容するようになります。したがって、結果として得られるアプリケーションはより堅牢になります。したがって、IEEE Std 1003.1-2001は、誤ったウェイクアップが発生する可能性があることを明示的に文書化しています。

my_better_condition_variableしたがって、基本的には、pthreads条件変数(またはstd::condition_variable)の上に、パフォーマンスを低下させることなく、かなり簡単に構築できるという主張があります。ただしmy_better_condition_variable、基本レベルに置くと、の機能を必要としないクライアントはmy_better_condition_variableとにかくそれを支払う必要があります。

最速で最も原始的なデザインをスタックの一番下に置くというこの哲学は、より良い/より遅いものをそれらの上に構築できることを意図して、C++lib全体で実行されます。そして、C ++ libがこの哲学に従わない場合、クライアントはしばしば(そして当然のことながら)イライラします。

于 2012-06-15T00:35:40.513 に答える
6

ループを記述したくない場合は、代わりに述語をとるオーバーロードを使用できます。

cv.wait(lock, [&q]{ return !q.is_empty(); });

ループと同等に定義されているため、元のコードと同じように機能します。

于 2012-06-15T00:31:27.150 に答える
4

これを行うことができたとしても、C ++ 11仕様では、cv.wait()(その動作をするプラットフォームを説明するために)誤ってブロックを解除することができます。したがって、ハゲタカスレッドがない場合でも(存在するかどうかについての議論は別として)、コンシューマスレッドはCookieが待機していることを期待できず、チェックする必要があります。

于 2012-06-15T00:30:33.390 に答える