在C++中,std::condition_variable 条件变量是一个同步原语,它允许一个或多个线程在某个条件成立时,被另一个线程唤醒。std::condition_variable 条件变量通常与互斥锁(std::mutex)一起使用,以保护共享数据和同步线程之间的通信。
条件变量的使用场景
条件变量主要用于以下场景:
- 等待/通知模型:一个或多个线程等待某个条件成立,另一个线程在条件成立时通知等待的线程。
- 生产者/消费者问题:生产者线程生产数据后通知消费者线程,消费者线程等待生产者生产数据。
条件变量的基本使用步骤
- 定义互斥锁和条件变量:首先,你需要定义一个互斥锁(例如std::mutex)和一个 std::condition_variable 条件变量。
- 锁定互斥锁:在调用 std::condition_variable 条件变量的 wait 或 wait_for 等函数之前,必须先锁定互斥锁。
- 调用条件变量的等待函数:调用 wait、wait_for 或 wait_until 等函数来等待条件成立。这些函数会释放互斥锁,使其他线程可以进入临界区,并在条件变量上等待。当其他线程调用 std::condition_variable 条件变量的 notify_one 或 notify_all 来唤醒等待的线程时,等待的线程会重新获取互斥锁并继续执行。
- 检查条件:在 wait、wait_for 或 wait_until 返回后,通常需要重新检查条件是否满足,因为可能存在虚假唤醒(spurious wakeup)的情况。
- 解锁互斥锁:在离开临界区时,需要解锁互斥锁。
使用示例
以下是一个简单的生产者/消费者问题的示例,展示了 std::condition_variable 条件变量的使用:
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
std::mutex g_mtx;
std::condition_variable g_cv;
std::queue<int> g_queue;
bool g_done = false;
// 生产者函数
void producer(int id) {
for (int i = 0; i < 10; ++i) {
std::unique_lock<std::mutex> lck(g_mtx);
g_queue.push(id * 10 + i);
std::cout << "Produced: " << id * 10 + i << std::endl;
lck.unlock();
g_cv.notify_one(); // 通知一个等待的线程
}
{
std::lock_guard<std::mutex> lck(g_mtx);
g_done = true;
}
g_cv.notify_all(); // 通知所有等待的线程
}
// 消费者函数
void consumer() {
std::unique_lock<std::mutex> lck(g_mtx);
while (!g_done && g_queue.empty()) {
g_cv.wait(lck); // 等待条件成立
}
while (!g_queue.empty()) {
int val = g_queue.front();
g_queue.pop();
std::cout << "Consumed: " << val << std::endl;
lck.unlock();
// 模拟耗时操作,等待100毫秒
std::this_thread::sleep_for(std::chrono::milliseconds(100));
lck.lock();
}
}
int main() {
std::thread producers[2]; // 生产者线程池
std::thread consumers[5]; // 消费者线程池
// 创建生产者
for (int i = 0; i < 2; ++i) {
producers[i] = std::thread(producer, i);
}
// 创建消费者
for (int i = 0; i < 5; ++i) {
consumers[i] = std::thread(consumer);
}
// 等待生产者完成
for (auto& t : producers) {
t.join();
}
// 唤醒所有等待的消费者(尽管这里可能不是必需的,因为done标志已经设置)
g_cv.notify_all();
// 等待消费者完成
for (auto& t : consumers) {
t.join();
}
return 0;
}
在这个例子中,生产者和消费者线程使用条件变量(g_cv)和互斥锁(g_mtx)来同步对共享队列(g_queue)的访问。生产者生产数据后,通过调用notify_one来唤醒一个等待的消费者线程。当所有生产者都完成生产后,它们将done标志设置为true,并通过调用`notify_all 来唤醒所有的消费者线程。
-End-
#想了解更多精彩内容,关注下方公众号。
本人小杨哥:
超20年C++开发经验,独立软件开发;著名开源产品高并发C++应用服务器MYCP作者;开源企业即时通讯软件Entboost首席架构师;开发有【WordBN字远笔记】等共享软件产品。