簡単にするために、ブール値によって反映される単一の条件に一致する条件変数が1つだけであると仮定します。
1)スリープを解除するために「通知」が送信された後、std::condition_variable::wait(...)
がミューテックスを再度ロックするのはなぜですか?
2)「1)」の動作を見ると、_std::condition_variable::notify_all
_を実行すると、待機中のすべてのスレッドがブロック解除/ウェイクアップされるようになるだけです...しかし順番に一度に全部ではなく?もしそうなら、それを一度に行うために何ができるでしょうか?
3)条件が満たされるまでスレッドがスリープすることだけを気にし、ミューテックスの取得を1ビットも気にしない場合、どうすればよいですか?代替手段はありますか、または現在のstd::condition_variable::wait(...)
アプローチをハッキングする必要がありますか?
「ハッカリー」を使用する場合、この関数はブロックを解除するために機能しますかall条件付きの待機中のスレッドであり、任意の(スレッドごとの)スレッドから呼び出すことができますか?
_//declared somehwere and modified before sending "notify"(ies)
std::atomic<bool> global_shared_condition_atomic_bool;
//the single(for simplicity in our case) condition variable matched with the above boolean result
std::condition_variable global_shared_condition_variable;
static void MyClass:wait()
{
std::mutex mutex;
std::unique_lock<std::mutex> lock(mutex);
while (!global_shared_condition_atomic_bool) global_shared_condition_variable.wait(lock);
}
_
次のように、ランダムな「待機中」のスレッドから呼び出されます。
_void random_thread_run()
{
while(someLoopControlValue)
{
//random code...
MyClass:wait(); //wait for whatever condition the class+method is for.
//more random code...
}
}
_
編集:
ゲートクラス
_#ifndef Gate_Header
#define Gate_Header
#include <mutex>
#include <condition_variable>
class Gate
{
public:
Gate()
{
gate_open = false;
}
void open()
{
m.lock();
gate_open = true;
m.unlock();
cv.notify_all();
}
void wait()
{
std::unique_lock<std::mutex> lock(m);
while (!gate_open) cv.wait(lock);
}
void close()
{
m.lock();
gate_open = false;
m.unlock();
}
private:
std::mutex m;
std::condition_variable cv;
bool gate_open;
};
#endif
_
条件変数は、物事を誤って目覚めさせます。
あなたは必須ミューテックスを持っていて、それが必須何らかのメッセージを保護して動作させるか、そのようなウェイクアップが発生したという保証はありません。
これが行われたのは、おそらく、非スプリアスバージョンの効率的な実装が、とにかくそのようなスプリアスバージョンの観点から実装されてしまうためです。
ミューテックスでメッセージ編集を保護できない場合(つまり、同期がない場合、メッセージの状態は未定義の動作です。これにより、コンパイラはメモリからの読み取りを最適化して、最初の読み取り後にスキップする可能性があります。
その未定義の動作(アトミックを使用することを想像してください)を除いても、メッセージが設定され、通知が発生し、通知を待機している人は、変数が設定され、条件変数が通知されます。
極端な場合を除いて、通常はラムダバージョンのwait
を使用します。
通知コードと待機コードの両方を監査しない限り、条件変数コードを監査することはできません。
struct gate {
bool gate_open = false;
mutable std::condition_variable cv;
mutable std::mutex m;
void open_gate() {
std::unique_lock<std::mutex> lock(m);
gate_open=true;
cv.notify_all();
}
void wait_at_gate() const {
std::unique_lock<std::mutex> lock(m);
cv.wait( lock, [this]{ return gate_open; } );
}
};
または
void open_gate() {
{
std::unique_lock<std::mutex> lock(m);
gate_open=true;
}
cv.notify_all();
}
いいえ、コードは機能しません。
mutex
は、共有変数への変更を保護します。そのため、すべての待機スレッドとシグナリングスレッドは、その特定のmutex
インスタンスをロックする必要があります。あなたが書いたもので、各スレッドはそれ自身のmutex
インスタンスを持っています。
このすべてのmutex
のものの主な理由は、条件変数のOS実装の不幸な側面である スプリアスウェイクアップ の概念によるものです。それらを待機しているスレッドは、条件がまだ満たされていない場合でも、実行を開始することがあります。
実際の変数のmutex
-boundチェックにより、スレッドはそれが誤って起こされたかどうかをテストできます。
wait
はmutex
をアトミックに解放し、条件の待機を開始します。 wait
が終了すると、ウェイクアッププロセスの一部としてmutex
がアトミックに再取得されます。ここで、偽のウェイクアップと通知スレッドの間の競争について考えてみましょう。通知スレッドは、変数を変更しようとしている状態、または変数を変更して全員にウェイクアップを通知しようとしている状態のいずれかになります。
通知スレッドが変数を変更しようとしているときに誤ったウェイクアップが発生した場合、そのうちの1つが最初にmutex
に到達します。したがって、誤って起動されたスレッドには、古い値または新しい値が表示されます。それが新しいものを見た場合、それは通知されており、そのビジネスを行います。古いものを見ると、再びその状態を待ちます。しかし、古いものを見た場合は、通知スレッドがその変数を変更するのをブロックしたため、偽のスレッドがスリープ状態に戻るまで待機する必要がありました。
「notify」が送信されてスリープを解除した後、std :: condition_variable :: wait(...)がミューテックスを再びロックするのはなぜですか?
mutex
は条件変数へのアクセスをロックするためです。そして、wait
呼び出しから目覚めた後、最初にやらなければならないことは、条件変数をチェックすることです。そのため、これはmutex
の保護の下で実行する必要があります。
シグナリングスレッドは、他のスレッドが変数を読み取っている間、変数を変更できないようにする必要があります。それがmutex
の目的です。
「1)」の動作を見ると、std :: condition_variable :: notify_allを実行すると、待機中のすべてのスレッドがブロック解除/ウェイクアップされるようになるだけです...ただし、一度にすべてではなく順番に?
彼らが目覚める順序は指定されていません。しかし、その時までにnotify_all
が返され、すべてのスレッドがブロック解除されていることが保証されます。
条件が満たされるまでスレッドがスリープすることだけを気にし、ミューテックスの取得を1ビットも気にしない場合、どうすればよいですか?
何もありません。 condition_variable
は、チェックしている実際の変数へのアクセスがmutex
を介して制御されることを要求します。