C++并发编程实战:条件变量深度解析与应用指南
引言
在多线程编程中,条件变量(Condition Variable)是一种重要的线程同步机制,它允许线程在某些条件不满足时主动阻塞,并在条件满足时被唤醒。本文将深入探讨C++标准库中的条件变量实现,帮助开发者掌握这一强大的并发编程工具。
条件变量基础概念
条件变量总是与互斥量(mutex)配合使用,主要解决线程间的"等待-通知"问题。其核心思想是:当某个条件不满足时,线程可以释放锁并进入等待状态;当其他线程改变了条件并发出通知时,等待的线程被唤醒并重新获取锁。
std::condition_variable详解
构造函数
std::condition_variable
只提供默认构造函数,拷贝构造函数被显式删除:
std::condition_variable cv1; // 正确
std::condition_variable cv2 = cv1; // 错误,拷贝构造被禁用
等待机制
基本wait操作
void wait(std::unique_lock<std::mutex>& lck);
当线程调用wait时,它会:
- 自动释放锁(lck.unlock())
- 进入阻塞状态等待通知
- 收到通知后重新获取锁(lck.lock())
带谓词的wait
template <class Predicate>
void wait(std::unique_lock<std::mutex>& lck, Predicate pred);
这种形式相当于:
while(!pred()) {
wait(lck);
}
只有当pred返回false时才阻塞,收到通知后只有当pred返回true才会继续执行。
定时等待
wait_for
template <class Rep, class Period>
cv_status wait_for(std::unique_lock<std::mutex>& lck,
const std::chrono::duration<Rep,Period>& rel_time);
在指定时间段内等待通知,超时返回cv_status::timeout
,否则返回cv_status::no_timeout
。
wait_until
template <class Clock, class Duration>
cv_status wait_until(std::unique_lock<std::mutex>& lck,
const std::chrono::time_point<Clock,Duration>& abs_time);
在指定时间点前等待通知,行为与wait_for类似。
通知机制
notify_one
唤醒一个等待线程。如果有多个线程在等待,选择哪个线程是不确定的。
notify_all
唤醒所有等待线程。
条件变量使用模式
生产者-消费者模式
std::mutex mtx;
std::condition_variable cv;
std::queue<int> data_queue;
void producer() {
while(true) {
std::unique_lock<std::mutex> lock(mtx);
data_queue.push(42);
cv.notify_one();
}
}
void consumer() {
while(true) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{return !data_queue.empty();});
int data = data_queue.front();
data_queue.pop();
// 处理数据
}
}
线程池任务分发
std::vector<std::thread> workers;
std::mutex queue_mutex;
std::condition_variable condition;
std::queue<std::function<void()>> tasks;
void worker_thread() {
while(true) {
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(queue_mutex);
condition.wait(lock, []{return !tasks.empty();});
task = tasks.front();
tasks.pop();
}
task();
}
}
std::condition_variable_any
std::condition_variable_any
是更通用的条件变量实现,可以与任何满足Lockable概念的类型配合使用,而不仅限于std::unique_lock<std::mutex>
。但在性能上通常略逊于std::condition_variable
。
常见陷阱与最佳实践
-
虚假唤醒:即使没有通知,等待的线程也可能被唤醒。因此总是使用带谓词的wait形式。
-
丢失唤醒:在调用wait之前发出通知会导致通知丢失。确保状态改变和通知是原子操作。
-
锁的作用域:通知时不需要持有锁,实际上不持有锁时通知效率更高。
-
性能考虑:频繁的信号可能引起"惊群效应",考虑是否需要notify_all或notify_one。
总结
条件变量是C++多线程编程中强大的同步原语,正确使用可以高效解决复杂的线程同步问题。理解其工作原理和使用模式,能够帮助开发者编写出更健壮、高效的并发程序。记住核心原则:总是与互斥量配合使用,正确处理虚假唤醒,并合理选择通知策略。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考