C++ 標準ライブラリ<condition_variable>
マルチスレッドプログラミングにおいて、スレッド間の同期は非常に重要な問題です。
C++11 標準では、<condition_variable>ヘッダーファイルが導入され、それは、スレッドが特定の条件を満たしていないときに一時停止し、他のスレッドが条件が満たされたことを通知するまで待機できるようにするメカニズムを提供します。
condition_variableそれは、スレッド間同期のための高水準なツールであり、ミューテックスや条件変数などの低水準の同期プリミティブを使用するよりも安全で便利です。
condition_variableこれはクラステンプレートであり、マルチスレッド環境でスレッド間の同期を実装するために使用されます。1つまたは複数のスレッドが特定の条件が真になるのを待ち、他のスレッドがこれらの待機中のスレッドを起こすことができます。
構文
以下はcondition_variableの基本構文です:
#include <condition_variable>
void notify_one() {
// 唤醒一个等待的线程
}
void notify_all() {
// 唤醒所有等待的线程
}
template <class Mutex>
class condition_variable {
public:
condition_variable();
~condition_variable();
void wait(unique_lock<mutex>& lock);
void wait_for(unique_lock<mutex>& lock, chrono::duration<Rep, Period> const& rel_time);
void wait_until(unique_lock<mutex>& lock, chrono::time_point<Clock, Duration> const& abs_time);
void notify_one() noexcept;
void notify_all() noexcept;
};
例
以下は、...を使用したcondition_variable簡単な例で、プロデューサー・コンシューマー問題の基本的な実装を示しています。
例
#include <iostream>
#include <condition_variable>
#include <mutex>
#include <queue>
#include <thread>
std::mutex mtx;
std::condition_variable cv;
std::queue<int> product;
void producer(int id) {
for (int i = 0; i < 5; ++i) {
std::unique_lock<std::mutex> lck(mtx);
product.push(id * 100 + i);
std::cout << "Producer " << id << " produced " << product.back() << std::endl;
cv.notify_one();
lck.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
void consumer(int id) {
while (true) {
std::unique_lock<std::mutex> lck(mtx);
cv.wait(lck, []{ return !product.empty(); });
if (!product.empty()) {
int prod = product.front();
product.pop();
std::cout << "Consumer " << id << " consumed " << prod << std::endl;
}
lck.unlock();
}
}
int main() {
std::thread producers[2];
std::thread consumers[2];
for (int i = 0; i < 2; ++i) {
producers[i] = std::thread(producer, i + 1);
}
for (int i = 0; i < 2; ++i) {
consumers[i] = std::thread(consumer, i + 1);
}
for (int i = 0; i < 2; ++i) {
producers[i].join();
consumers[i].join();
}
return 0;
}
#include <condition_variable>
#include <mutex>
#include <queue>
#include <thread>
std::mutex mtx;
std::condition_variable cv;
std::queue<int> product;
void producer(int id) {
for (int i = 0; i < 5; ++i) {
std::unique_lock<std::mutex> lck(mtx);
product.push(id * 100 + i);
std::cout << "Producer " << id << " produced " << product.back() << std::endl;
cv.notify_one();
lck.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
void consumer(int id) {
while (true) {
std::unique_lock<std::mutex> lck(mtx);
cv.wait(lck, []{ return !product.empty(); });
if (!product.empty()) {
int prod = product.front();
product.pop();
std::cout << "Consumer " << id << " consumed " << prod << std::endl;
}
lck.unlock();
}
}
int main() {
std::thread producers[2];
std::thread consumers[2];
for (int i = 0; i < 2; ++i) {
producers[i] = std::thread(producer, i + 1);
}
for (int i = 0; i < 2; ++i) {
consumers[i] = std::thread(consumer, i + 1);
}
for (int i = 0; i < 2; ++i) {
producers[i].join();
consumers[i].join();
}
return 0;
}
このプログラムの出力は、スレッドのスケジューリングに依存するため、異なる場合があります。しかし、生産者が製品を生産し、次に消費者がそれらの製品を消費するのがわかるでしょう。出力は次のようになります:
Producer 1 produced 100 Producer 2 produced 200 Consumer 1 consumed 100 Producer 1 produced 101 Consumer 2 consumed 200 Producer 2 produced 201 Consumer 1 consumed 101 ...
注意事項
- 使用する
condition_variable場合、待機する前にミューテックスを取得し、起こされた後にミューテックスを解放することを確認する必要があります。 wait、wait_forとwait_untilすべての関数は、ミューテックスを解放し、待機中に再取得します。notify_one待機中の1つのスレッドを起こす一方、notify_all待機中のすべてのスレッドを起こします。