1. 条件变量基础概念与应用场景
在C++多线程编程中,条件变量(condition_variable)是一种关键的线程同步机制。它允许线程在特定条件不满足时主动挂起,直到其他线程通知条件可能已经满足。这种机制比传统的忙等待(busy-waiting)方式高效得多,因为它不会浪费CPU周期。
1.1 为什么需要条件变量
想象一个典型的生产者-消费者场景:消费者线程需要等待队列中有数据才能进行处理。如果没有条件变量,消费者线程可能会这样实现:
cpp复制while (queue.empty()) {} // 忙等待,CPU占用率100%
process(queue.front());
这种实现方式会导致CPU资源被白白浪费。条件变量的引入正是为了解决这个问题,它提供了以下核心能力:
- 让线程在条件不满足时主动阻塞
- 当条件可能满足时唤醒等待的线程
- 与互斥锁配合保证线程安全
1.2 条件变量与互斥锁的关系
条件变量总是与互斥锁(mutex)配合使用,这种组合形成了多线程编程中的经典模式:
cpp复制std::mutex mtx;
std::condition_variable cv;
bool ready = false;
// 等待线程
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return ready; });
// 通知线程
{
std::lock_guard<std::mutex> lock(mtx);
ready = true;
}
cv.notify_one();
这种设计确保了:
- 检查条件和修改条件都在互斥锁保护下进行
- 等待期间会释放锁,避免死锁
- 被唤醒后会重新获取锁,保证线程安全
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 条件变量的核心操作详解
2.1 基本等待操作:wait()
wait()是条件变量最基础的操作,它有两个重载版本:
2.1.1 无谓词版本
cpp复制void wait(std::unique_lock<std::mutex>& lock);
这个版本会:
- 原子地释放锁并阻塞当前线程
- 被唤醒后重新获取锁
- 可能因虚假唤醒而返回
典型使用模式:
cpp复制std::unique_lock<std::mutex> lock(mtx);
while (!condition) {
cv.wait(lock);
}
2.1.2 带谓词版本(推荐)
cpp复制template<class Predicate>
void wait(std::unique_lock<std::mutex>& lock, Predicate pred);
这个版本解决了虚假唤醒问题,内部会自动处理条件检查:
cpp复制std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return ready; });
实际开发中强烈建议使用带谓词的版本,它能自动处理虚假唤醒,代码更简洁安全。
2.2 限时等待操作
C++提供了两种限时等待方式,分别基于相对时间和绝对时间。
2.2.1 wait_for() - 相对时间等待
cpp复制template<class Rep, class Period>
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,
const std::chrono::duration<Rep, Period>& rel_time,
Predicate pred);
使用示例:
cpp复制// 无谓词版本
auto status = cv.wait_for(lock, std::chrono::seconds(1));
if (status == std::cv_status::timeout) {
// 超时处理
}
// 带谓词版本(推荐)
bool result = cv.wait_for(lock, std::chrono::seconds(1), []{ return ready; });
if (!re
