notify_one()
notify_all()
ブロックされたスレッドをウェイクアップするために一般的に使用されるのとは異なり、スレッドはウェイクアップされた後すぐにロックを取得しようとします。
notify_one()
起動されるスレッドは1つだけなので、ロックの競合は発生せず、すぐにロックを取得できます。残りのスレッドは起動されず、別の呼び出しnotify_one()
またはを待つ必要がありnotify_all()
ます。
notify_all()
ブロックされたすべてのスレッドが起動され、ロックの競合が発生し、1つのスレッドのみがロックを取得できます。ロックを取得していない残りのスレッドはどうなりますか?ブロックしますか?または、ロックを取得しようとし続けますか?答えは、再度ブロックすることなく、(ポーリングと同様に)ロックを取得しようとし続けることです。ロックを保持しているスレッドがロックを解放すると、これらのスレッドの1つがロックを取得します。その後、残りはロックを取得しようとします。
次の例を見てください。
// condition_variable example
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void print_id(int id) {
std::unique_lock<std::mutex> lck(mtx);
while (!ready) cv.wait(lck);
// ...
std::cout << "thread " << id << '\n';
}
void go() {
std::unique_lock<std::mutex> lck(mtx);
ready = true;
cv.notify_all(); // 这是重点
}
int main()
{
std::thread threads[10];
// spawn 10 threads:
for (int i = 0; i < 10; ++i)
threads[i] = std::thread(print_id, i);
std::cout << "10 threads ready to race...\n";
go(); // go!
for (auto& th : threads) th.join();
return 0;
}
操作の結果は次のとおりです。
10 threads ready to race...
thread 2
thread 0
thread 9
thread 4
thread 6
thread 8
thread 7
thread 5
thread 3
thread 1
出力は、すべてのスレッドがウェイクアップされた後もロックを取得したことを示しています。
場合変更、結果は以下のとおりです。go()
cv.notify_all()
cv.notify_one()
10 threads ready to race...
thread 0
出力は、1つのスレッドのみが起動され、スレッドがロックを解放したことを示します。この時点で、ロックはロック解除状態で処理されていますが、残りのスレッドはまだブロックされた状態です。
したがって、スレッドがブロックされると、ロックがロックされてウェイクアップするのを待つのではなく、condition_variable
待機notify_one()
またはnotify_all()
ウェイクアップします。スレッドがウェイクアップされた後、スレッドはポーリングによってロックを取得し、実行状態はロックが取得されるまで処理され、再度ブロックされることはありません。