<condition_variable>
是 C++ 标准库中用于线程同步的高级工具,主要用于在线程间协调共享数据的状态变化。借助条件变量(condition variable),线程可以在等待特定条件满足时阻塞自身,并在条件满足后被通知唤醒,从而实现高效的线程协作。
目录
<condition_variable>
头文件简介- 条件变量基础
- 使用条件变量进行线程同步
- 3.1 等待(wait)操作
- 3.2 通知(notify)操作
- 示例:生产者-消费者模型
- 常见注意事项
- 结论
1. <condition_variable>
头文件简介
条件变量用于线程间的通信,其主要思想是允许一个或多个线程等待直到另一个线程发送信号。通过使用 <condition_variable>
,可以避免忙等待(busy waiting),提高程序的效率和响应性。要使用条件变量,需要包含该头文件:
#include <condition_variable>
2. 条件变量基础
2.1 std::condition_variable
- 功能:用于线程间等待和通知。常与互斥量(如
std::mutex
)配合使用,保护共享数据的访问。 - 典型用法:线程调用
wait()
函数等待条件满足,而其他线程调用notify_one()
或notify_all()
发出通知。
2.2 std::condition_variable_any
- 功能:与
std::condition_variable
类似,但可以与任何满足 Lockable 概念的锁配合使用,而不仅限于std::unique_lock<std::mutex>
。
3. 使用条件变量进行线程同步
条件变量的常见操作主要有两类:等待和通知。
3.1 等待(wait)操作
等待操作通常与 std::unique_lock
一起使用。线程在等待之前会释放互斥量,等待被通知后,再重新获取锁继续执行。
基本用法示例:
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void waiting_thread() {
std::unique_lock<std::mutex> lock(mtx);
// 等待直到 ready 为 true
cv.wait(lock, [] { return ready; });
std::cout << "Condition satisfied, proceeding..." << std::endl;
}
在上述代码中,线程调用 cv.wait(lock, [] { return ready; });
,会释放 mtx
并等待通知。当其他线程修改 ready
为 true
并发出通知时,该线程将重新获取锁并继续执行。
3.2 通知(notify)操作
通知操作用于唤醒等待的线程,主要有两种:
notify_one()
:唤醒一个等待线程。notify_all()
:唤醒所有等待线程。
通知示例:
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
extern bool ready;
extern std::mutex mtx;
extern std::condition_variable cv;
void signaling_thread() {
{
std::lock_guard<std::mutex> lock(mtx);
ready = true; // 修改共享数据状态
}
cv.notify_all(); // 通知所有等待线程
std::cout << "Notified waiting threads." << std::endl;
}
4. 示例:生产者-消费者模型
以下示例展示了如何使用条件变量实现简单的生产者-消费者模型。生产者生成数据并通知消费者,而消费者等待数据的产生。
#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
std::queue<int> dataQueue;
std::mutex mtx;
std::condition_variable cv;
bool finished = false;
void producer() {
for (int i = 1; i <= 10; ++i) {
{
std::lock_guard<std::mutex> lock(mtx);
dataQueue.push(i);
std::cout << "Produced: " << i << std::endl;
}
cv.notify_one(); // 通知等待的消费者
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
{
std::lock_guard<std::mutex> lock(mtx);
finished = true;
}
cv.notify_all(); // 通知所有消费者生产结束
}
void consumer() {
while (true) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [] { return !dataQueue.empty() || finished; });
while (!dataQueue.empty()) {
int data = dataQueue.front();
dataQueue.pop();
lock.unlock(); // 在处理数据前释放锁以提高并发性
std::cout << "Consumed: " << data << std::endl;
lock.lock();
}
if (finished && dataQueue.empty())
break;
}
}
int main() {
std::thread prod(producer);
std::thread cons(consumer);
prod.join();
cons.join();
std::cout << "Processing complete." << std::endl;
return 0;
}
在这个示例中:
- 生产者将数据压入队列,并在每次生产后通知消费者。
- 消费者在等待队列非空或生产结束的条件下被唤醒,然后取出数据进行处理。
5. 常见注意事项
- 保护共享数据:在等待和通知过程中,必须使用互斥量保护共享数据的访问,防止数据竞争。
- 避免虚假唤醒:总是将
wait()
与条件判断结合使用,使用带有谓词版本的wait(lock, predicate)
可以自动处理虚假唤醒问题。 - 通知后释放锁:在调用
notify_one()
或notify_all()
前修改共享数据后,应尽快释放锁,确保等待线程能尽快获得锁继续执行。
6. 结论
条件变量是 C++ 多线程编程中实现线程间协调的重要机制。通过 <condition_variable>
,你可以编写高效且响应迅速的多线程应用程序,避免忙等待,并实现线程安全的等待和通知机制。掌握条件变量的使用将帮助你构建更加健壮和高效的并发程序。
推荐阅读:
发表回复