当前位置: 首页 > news >正文

c++多线程(6)------ 条件变量

  • 操作系统:ubuntu22.04
  • IDE:Visual Studio Code
  • 编程语言:C++11

条件变量(Condition Variable) 是实现线程间高效等待与通知机制的核心工具,通常与互斥锁配合使用,用于解决“生产者-消费者”、“任务队列”、“线程协调”等经典并发问题。

一、为什么需要条件变量?

❌ 问题:忙等待(Busy Waiting)效率低

std::mutex mtx;
bool ready = false;// 线程A(等待者)
while (!ready)
{// 空循环,持续检查
}
// do work...// 线程B(通知者)
{std::lock_guard<std::mutex> lock(mtx);ready = true;
}
  • CPU 资源浪费:等待线程不断轮询,占用 CPU。
  • 响应延迟:无法立即响应状态变化。

✅ 解决方案:条件变量

  • 等待线程挂起(阻塞),不消耗 CPU。
  • 通知线程唤醒等待线程。
  • 高效、节能、响应及时。

二、C++11 中的条件变量类型

C++11 在 <condition_variable> 中提供了两种条件变量:

类型说明
std::condition_variable仅支持 std::unique_lockstd::mutex,性能更高,最常用
std::condition_variable_any支持任意满足 BasicLockable 的锁(如 shared_mutex),但性能略低

✅ 推荐优先使用 std::condition_variable。

三、基本用法:wait() 与 notify_one() / notify_all()

核心 API:

void wait(std::unique_lock<std::mutex>& lock);
template<class Predicate>
void wait(std::unique_lock<std::mutex>& lock, Predicate pred);
void notify_one();  // 唤醒一个等待线程
void notify_all();  // 唤醒所有等待线程

🌰 示例:简单线程同步

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>std::mutex mtx;
std::condition_variable cv;
bool ready = false;void worker()
{std::unique_lock<std::mutex> lock(mtx);cv.wait(lock, []{ return ready; }); // 等待 ready == truestd::cout << "Worker: working now!\n";
}void starter() 
{std::this_thread::sleep_for(std::chrono::seconds(1));{std::lock_guard<std::mutex> lock(mtx);ready = true;}cv.notify_one(); // 通知一个等待线程
}int main() 
{std::thread t1(worker);std::thread t2(starter);t1.join(); t2.join();
}

输出:

Worker: working now!

四、深入理解 wait() 的工作机制

cv.wait(lock, pred) 等价于:

while (!pred()) 
{cv.wait(lock); // 内部:1. unlock mutex; 2. 阻塞等待; 3. 被唤醒后重新 lock
}

关键点:

  • 自动释放锁:调用 wait() 时,会自动释放传入的 unique_lock,允许其他线程获取锁并修改共享状态。
  • 原子性:释放锁 + 进入等待 是原子操作,避免通知丢失(lost wake-up)。
  • 虚假唤醒(Spurious Wakeup):即使没有调用 notify,线程也可能被唤醒(POSIX 允许)。因此必须使用谓词(predicate) 检查条件。

✅ 所以永远不要写 cv.wait(lock); 而不带谓词!

五、经典应用:生产者-消费者模型(任务队列)

#include <chrono>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <queue>
#include <thread>std::queue< int > task_queue;
std::mutex mtx;
std::condition_variable cv;
bool done = false;// 生产者
void producer( int id )
{for ( int i = 0; i < 5; ++i ){{std::lock_guard< std::mutex > lock( mtx );task_queue.push( id * 10 + i );std::cout << "Producer " << id << " produced " << id * 10 + i << "\n";}cv.notify_one();  // 通知消费者std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );}
}// 消费者
void consumer( int id )
{while ( true ){std::unique_lock< std::mutex > lock( mtx );cv.wait( lock, [] { return !task_queue.empty() || done; } );if ( done && task_queue.empty() )break;int task = task_queue.front();task_queue.pop();lock.unlock();  // 提前释放锁,减少临界区std::cout << "Consumer " << id << " processed " << task << "\n";std::this_thread::sleep_for( std::chrono::milliseconds( 150 ) );}
}int main()
{std::thread p1( producer, 1 );std::thread p2( producer, 2 );std::thread c1( consumer, 1 );std::thread c2( consumer, 2 );p1.join();p2.join();done = true;cv.notify_all();  // 通知所有消费者退出c1.join();c2.join();
}

输出:

Producer 1 produced 10
Consumer 2 processed 10Producer 
2 produced 20
Consumer 1 processed 20
Producer 1 produced 11
Producer 2 produced 21
Consumer 2 processed 11
Consumer 1 processed 21
Producer 1 produced 12
Producer 2 produced 22
Consumer 2 processed 12
Consumer 1 processed 22
Producer 1 produced 13
Producer 2 produced 23
Producer 1 produced 14
Producer 2 produced 24
Consumer 2 processed 13
Consumer 1 processed 23
Consumer 1 processed 14
Consumer 2 processed 24

关键设计:

  • 使用 done 标志优雅退出。
  • cv.notify_one():一个任务唤醒一个消费者(避免惊群)。
  • 消费后提前解锁,提高并发性。

六、带超时的等待:wait_for() 与 wait_until()

适用于“最多等待 N 秒”的场景。

std::unique_lock<std::mutex> lock(mtx);
if (cv.wait_for(lock, std::chrono::seconds(5), []{ return ready; })) 
{std::cout << "Condition met!\n";
}
else 
{std::cout << "Timeout! Condition not met.\n";
}
  • wait_for:相对时间(duration)
  • wait_until:绝对时间(time_point)

✅ 返回值:若因条件满足而唤醒,返回 true;若超时,返回 false。

七、常见错误与最佳实践

❌ 错误1:忘记使用谓词(导致虚假唤醒崩溃)

// 危险!可能虚假唤醒后继续执行
cv.wait(lock);
// 此时 ready 可能仍为 false!

✅ 正确:

cv.wait(lock, []{ return ready; });

❌ 错误2:在未加锁时修改条件并通知

ready = true;           // ❌ 未加锁!
cv.notify_one();        // 可能通知丢失

✅ 正确:

{std::lock_guard<std::mutex> lock(mtx);ready = true;
}
cv.notify_one(); // 通知可在锁外,但修改必须在锁内

📝 通知可以在锁外调用(C++ 允许),但修改共享状态必须在锁内。

❌ 错误3:使用 std::lock_guard 与 wait()

std::lock_guard<std::mutex> lock(mtx);
cv.wait(lock, ...); // ❌ 编译错误!wait 需要 unique_lock

✅ 正确:

std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, ...);

❌ 错误4:滥用 notify_all()

  • 若只有一个线程能处理任务,用 notify_one() 更高效。
  • notify_all() 适用于广播场景(如所有线程需响应“退出”信号)。

八、notify_one() vs notify_all()

场景推荐
任务队列(一个任务 → 一个消费者)notify_one()
状态变更需所有线程响应(如 shutdown)notify_all()
不确定有多少线程在等notify_all()(安全但低效)

九、总结:条件变量使用模板

// 共享状态
bool condition = false;
std::mutex mtx;
std::condition_variable cv;// 等待线程
{std::unique_lock<std::mutex> lock(mtx);cv.wait(lock, []{ return condition; });// 条件满足,处理逻辑
}// 通知线程
{std::lock_guard<std::mutex> lock(mtx);condition = true;
}
cv.notify_one(); // 或 notify_all()

最佳实践清单:

  • 总是使用谓词版本的 wait()。
  • 修改共享状态必须在互斥锁保护下。
  • 使用 std::unique_lock(不是 lock_guard)。
  • 优先用 notify_one(),除非需要广播。
  • 考虑超时机制(wait_for)避免永久阻塞。
  • 避免在持有锁时做耗时操作(提前 unlock)。
http://www.dtcms.com/a/461037.html

相关文章:

  • 旅游网站建设与网页设计大连做网站报价
  • 网站排名英文服装加工厂网站建设方案计划书
  • 专题:2025中国人工智能医疗健康研究报告(智慧医院+医保+器械+AI)深度洞察|附130+份报告PDF、8个数据仪表盘汇总下载
  • 【学习记录】vscode+ros2+cpp调试
  • 性价比高的热冷分离真空共晶炉企业
  • 嵌入式分享#41:RK3576改UART波特率【精简版】
  • pc端pdf预览
  • 网站建设衤金手指花总十五wordpress电商主题数据
  • 【STM32项目开源】基于STM32的智能天然气火灾监控
  • Git 补丁完整指南:创建、发送和应用补丁详解
  • python中f-string详解
  • C++网络编程(十三)epoll如何设置边缘模式
  • 一流的高端企业网站网站建设与维护是什么意思
  • 上海做响应式网站的公司做后台系统的网站
  • 服务端与客户端的简单链接
  • Ubuntu24.04系统安装图形化桌面并使用xrdp远程桌面
  • 无人机通信链路技术要点与难点
  • 计算机二级45天通关秘籍:高效备考策略与编程语言选择指南​
  • 测试基础01
  • 东莞住建局官网网站宁波网站建站
  • 网上的彩票网站是怎么做的网站建设基本问题
  • 微信小程序执行onPullDownRefresh 之后 下拉刷新后刷新标志不消失
  • 如何在网站后台备份数据库表台州智能模板建站
  • 寻梦数据空间 | 起源篇:从数据孤岛到互联新范式的战略演进
  • Bug、Bag、Buff 到底是什么意思?一篇看懂程序员的游戏黑话
  • 攻防世界-Web-easyphp
  • js移动端开发面试题
  • LoRaWAN NS 对比分析
  • php网站开发项目实战周易起名网唯一官网免费
  • PyCharm入门级详细使用手册(Python新手快速上手篇)