1. 条件变量基础概念与应用场景
在C++多线程编程中,条件变量(std::condition_variable)是线程同步的重要工具之一。它允许线程在某个条件不满足时主动进入等待状态,直到其他线程通知条件可能已经改变。这种机制从根本上解决了"忙等待"(busy-waiting)带来的CPU资源浪费问题。
1.1 为什么需要条件变量
想象一个典型的生产者-消费者场景:消费者线程需要从队列中取出数据,但当队列为空时,它不应该不断轮询检查队列状态(这会浪费CPU周期),而是应该进入休眠状态,直到有数据可用。条件变量正是为解决这类问题而设计的。
条件变量的核心价值体现在三个方面:
- 资源效率:避免了不必要的CPU占用
- 响应速度:当条件满足时能立即被唤醒
- 线程安全:与互斥锁配合确保共享数据的安全访问
1.2 条件变量的典型使用模式
条件变量通常与互斥锁(std::mutex)和共享状态变量配合使用,形成以下标准模式:
-
等待方(消费者):
- 获取互斥锁
- 检查条件(通常在循环中)
- 如果条件不满足,调用wait()释放锁并等待
- 被唤醒后重新检查条件
-
通知方(生产者):
- 获取互斥锁
- 修改共享状态
- 释放锁
- 调用notify_one()或notify_all()
这种模式确保了线程安全的同时,也保证了高效的线程协作。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. std::condition_variable核心API详解
2.1 等待函数:wait系列
2.1.1 基本wait
cpp复制void wait(std::unique_lock<std::mutex>& lock);
这是最基本的等待形式,它执行以下操作:
- 原子地释放锁并进入等待状态
- 被通知后重新获取锁
- 返回调用者
注意:使用基本wait时必须处理虚假唤醒问题,通常需要配合循环检查条件
2.1.2 带谓词的wait
cpp复制template<class Predicate>
void wait(std::unique_lock<std::mutex>& lock, Predicate pred);
这是推荐的使用方式,它内部已经处理了虚假唤醒问题。谓词pred应该是一个返回bool的可调用对象,在持有锁的情况下检查条件是否满足。
内部实现等价于:
cpp复制while(!pred()) {
wait(lock);
}
2.2 通知函数:notify系列
2.2.1 notify_one()
cpp复制void notify_one() noexcept;
唤醒一个正在等待的线程(如果有)。被唤醒的线程是不确定的,由系统调度决定。
2.2.2 notify_all()
cpp复制void notify_all() noexcept;
唤醒所有正在等待的线程。当多个线程可能同时满足条件时使用。
2.3 超时等待:wait_for和wait_until
2.3.1 wait_for
cpp复制template<class Rep, class Period>
std::cv_status wait_for(std::unique_lock<std::mutex>& lock,
const std::chrono::duration<Rep, Period>& rel_time);
template<class Rep, class Period, class Predicate>
bool wait_for(std::unique_lock<std::mutex>& lock,
con
