C++ 条件变量:wait、wait_for、wait_until
前言
在C++中,条件变量(std::condition_variable
)是用来在多个线程之间同步执行流的一种机制。它们通常与互斥锁(如std::mutex
)一起使用,以在特定条件满足时唤醒一个或多个线程。条件变量有三种使线程阻塞并等待唤醒的方法,分别是wait
,wait_for
,wait_until
三种方式,三种方式有不同的特点;
内容
Wait
- 功能:
wait
函数使当前线程阻塞,直到另一个线程调用notify_one
或notify_all
。 - 参数:它需要两个参数:一个
std::unique_lock<std::mutex>
(或类似的锁类型),它应该在调用wait
之前由调用线程锁定,并且在wait
等待期间由wait
自动解锁;以及一个函数或可调用对象(通常是一个lambda表达式或函数指针),用于检查条件是否满足。 - 特点:
wait
会无限期地等待,直到条件满足。它会在每次从wait
返回时重新获取互斥锁。
#include <mutex>
#include <condition_variable>
#include <thread>
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); // 等待ready变为true
}
// 当ready为true时,继续执行...
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];
// 创建10个线程
for (int i=0; i<10; ++i)
{
threads[i] = std::thread(print_id, i);
}
std::cout << "10 threads ready to race...\n";
go(); // 唤醒所有线程
// 等待所有线程完成
for (auto& th : threads)
{
th.join();
return 0;
}
}
Wait_for
- 功能:
wait_for
函数使当前线程阻塞一段指定的时间或直到另一个线程调用notify_one
或notify_all
,以先发生者为准。 - 参数:与
wait
类似,它需要一个std::unique_lock<std::mutex>
和一个函数或可调用对象来检查条件。此外,它还需要一个表示等待时间的std::chrono::duration
类型的参数。 - 特点:
wait_for
提供了等待时间的上限。如果在指定的时间内条件没有变为真,则wait_for
会返回,即使notify_one
或notify_all
还没有被调用。
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void print_id(int id)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Thread " << id << '\n';
}
void print_ready()
{
std::unique_lock<std::mutex> lck(mtx);
while (!ready)
{
// 循环直到条件满足
cv.wait_for(lck, std::chrono::seconds(2), []{ return ready; });
// 等待直到ready为true或超时
}
std::cout << "Ready now\n";
}
void go()
{
std::unique_lock<std::mutex> lck(mtx);
ready = true;
cv.notify_one();
// 唤醒一个等待的线程
}
int main()
{
std::thread threads[10];
// 启动10个线程,它们将等待ready标志
for (int i = 0; i < 10; ++i)
threads[i] = std::thread(print_ready);
std::cout << "10 threads ready to race...\n";
std::thread producer(go);
// 等待所有线程完成
for (auto& th : threads)
th.join();
producer.join();
return 0;
}
Wait_until
- 功能:
wait_until
函数使当前线程阻塞直到指定的时间点或直到另一个线程调用notify_one
或notify_all
,以先发生者为准。 - 参数:与
wait
和wait_for
类似,它需要一个std::unique_lock<std::mutex>
和一个函数或可调用对象来检查条件。此外,它还需要一个表示未来某个时间点的std::chrono::time_point
类型的参数。 - 特点:
wait_until
允许指定一个绝对的时间点作为等待的结束条件。如果在指定的时间点之前条件没有变为真,则wait_until
会返回,即使notify_one
或notify_all
还没有被调用。
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <system_clock>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void print_id(int id, const std::string& threadName) {
// 模拟一些工作
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << threadName << " " << id << std::endl;
}
void wait_for_ready(int id, const std::string& threadName) {
std::unique_lock<std::mutex> lck(mtx);
auto future_time = std::chrono::system_clock::now() + std::chrono::seconds(5); // 等待最多5秒
while (!ready) {
if (cv.wait_until(lck, future_time) == std::cv_status::timeout) {
std::cout << threadName << " " << id << " Timeout! Exiting.\n";
return;
}
}
// 如果ready为true,则继续执行
std::cout << threadName << " " << id << " Ready now.\n";
// 可以在这里处理数据...
}
void go() {
std::this_thread::sleep_for(std::chrono::seconds(3)); // 生产者准备数据需要一些时间
{
std::unique_lock<std::mutex> lck(mtx);
ready = true;
cv.notify_all(); // 唤醒所有等待的线程
}
// 生产者可以继续执行其他任务...
}
int main() {
std::thread threads[10];
// 启动10个消费者线程
for (int i = 0; i < 10; ++i) {
threads[i] = std::thread(wait_for_ready, i, "Consumer " + std::to_string(i+1));
}
std::thread producer(go);
// 等待所有消费者线程完成
for (auto& th : threads) {
th.join();
}
producer.join();
return 0;
}
总结
wait
:无限期等待直到条件满足。wait_for
:等待直到条件满足或指定的时间过去。wait_until
:等待直到条件满足或指定的时间点到达。