std::condition_variable
| ヘッダ <condition_variable> で定義
|
||
class condition_variable; |
(C++11以上) | |
condition_variable クラスは、別のスレッドが共有変数 (状態) を変更し condition_variable を通知するまで、1つまたは同時に複数のスレッドをブロックするために使用することができます。
変数を変更するつもりのスレッドは、以下のようにする必要があります。
- (一般的には std::lock_guard を使用して)
std::mutexを取得し、 - ロックを保持している間に変更を行い、
std::condition_variableに対して notify_one または notify_all を実行します (通知ためにロックを保持している必要はありません)。
共有変数がアトミックな場合でも、待機しているスレッドに変更を正しく公開するためには、ミューテックスの保護下で変更しなければなりません。
std::condition_variable で待機するつもりのスレッドは、以下のようにする必要があります。
- 共有変数を保護するために使用しているのと同じミューテックスの
std::unique_lock<std::mutex>を取得し、 - wait, wait_for または wait_until を実行します。 wait 操作は自動的にミューテックスを解放し、スレッドの実行を停止します。
- 条件変数が通知されるか、タイムアウトが満了するか、 spurious wakeup が発生すると、スレッドは起床し、ミューテックスは自動的に再取得されます。 その後、条件を確認し、起床が spurious であれば待機を再開します。
std::condition_variable は std::unique_lock<std::mutex> と組み合わせてのみ使用できます。 いくつかのプラットフォームではこの制限により最大の効率を発揮できます。 std::condition_variable_any を使用すれば、 BasicLockable な任意のオブジェクト、例えば std::shared_lock と組み合わせて使用できます。
メンバ関数 wait、wait_for、wait_until、notify_one、notify_all は並行的に呼び出すことができます。
クラス std::condition_variable は StandardLayoutType です。 CopyConstructible でも MoveConstructible でも CopyAssignable でも MoveAssignable でもありません。
メンバ型
| メンバ型 | 定義 |
native_handle_type
|
処理系定義 |
メンバ関数
| オブジェクトを構築します (パブリックメンバ関数) | |
| オブジェクトを破棄します (パブリックメンバ関数) | |
operator= [削除] |
コピー代入可能ではありません (パブリックメンバ関数) |
通知 | |
| 待機中のスレッドひとつに通知します (パブリックメンバ関数) | |
| 待機中のスレッドすべてに通知します (パブリックメンバ関数) | |
待機 | |
| 条件変数が通知されるまで現在のスレッドをブロックします (パブリックメンバ関数) | |
| 条件変数が通知されるか指定時間が経過するまで現在のスレッドをブロックします (パブリックメンバ関数) | |
| 条件変数が通知されるか指定時点に達するまで現在のスレッドをブロックします (パブリックメンバ関数) | |
ネイティブハンドル | |
| ネイティブハンドルを返します (パブリックメンバ関数) | |
例
スレッド間通信のために std::mutex と組み合わせて condition_variable を使用します。
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;
void worker_thread()
{
// main() からデータが送られるのを待ちます。
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return ready;});
// wait の後、ロックはこちらが所有した状態になっています。
std::cout << "Worker thread is processing data\n";
data += " after processing";
// データを main() に送り返します。
processed = true;
std::cout << "Worker thread signals data processing completed\n";
// 待機中のスレッドが起床直後に再度ブロックされるのを回避するために、
// 通知する前に手動でロックを解除します (詳細は notify_one を参照してください)。
lk.unlock();
cv.notify_one();
}
int main()
{
std::thread worker(worker_thread);
data = "Example data";
// ワーカースレッドにデータを送ります。
{
std::lock_guard<std::mutex> lk(m);
ready = true;
std::cout << "main() signals data ready for processing\n";
}
cv.notify_one();
// ワーカースレッドから送り返されるのを待ちます。
{
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return processed;});
}
std::cout << "Back in main(), data = " << data << '\n';
worker.join();
}
出力:
main() signals data ready for processing
Worker thread is processing data
Worker thread signals data processing completed
Back in main(), data = Example data after processing
あるスレッドからクラス内の別のグループにデータを送信するために同期プリミティブを使用します。
#include <condition_variable>
#include <mutex>
#include <thread>
#include <iostream>
#include <queue>
#include <chrono>
class condvarQueue
{
std::queue<int> produced_nums;
std::mutex m;
std::condition_variable cond_var;
bool done = false;
bool notified = false;
public:
void push(int i)
{
std::unique_lock<std::mutex> lock(m);
produced_nums.push(i);
notified = true;
cond_var.notify_one();
}
template<typename Consumer>
void consume(Consumer consumer)
{
std::unique_lock<std::mutex> lock(m);
while (!done) {
while (!notified) { // スプリアスウェイクアップを回避するためのループ
cond_var.wait(lock);
}
while (!produced_nums.empty()) {
consumer(produced_nums.front());
produced_nums.pop();
}
notified = false;
}
}
void close()
{
done = true;
notified = true;
cond_var.notify_one();
}
};
int main()
{
condvarQueue queue;
std::thread producer([&]() {
for (int i = 0; i < 5; ++i) {
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "producing " << i << '\n';
queue.push(i);
}
queue.close();
});
std::thread consumer([&]() {
queue.consume([](int input){
std::cout << "consuming " << input << '\n';
});
});
producer.join();
consumer.join();
}
出力:
producing 0
consuming 0
producing 1
consuming 1
producing 2
consuming 2
producing 3
consuming 3
producing 4
consuming 4