前言
在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
:等待直到条件满足或指定的时间点到达。