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

wait-notify之间做了什么

释放锁并进入等待(原子性阶段)

当你调用cv.wait(lock)时,底层会立即执行以下操作:

  • 释放锁:自动释放当前线程持有的std::unique_lock<std::mutex>
  • 加入队列:将当前线程放入该条件变量的等待队列中。
  • 进入休眠:挂起当前线程,不再消耗 CPU 资源。

核心细节:释放锁和进入等待这两个动作是原子性的。这意味着不会出现“刚释放锁,还没进入等待队列,通知就来了”的情况(即错失信号)。

被唤醒并尝试重新获取锁

当另一个线程调用cv.notify_one()cv.notify_all()时:

  • 唤醒:操作系统将线程从等待队列中移出,状态变为“就绪”。
  • 重新抢锁:线程在wait内部尝试重新获取(acquire)之前释放的那个mutex
  • 阻塞等待锁:如果锁此时被其他线程持有(比如通知者还没释放锁),被唤醒的线程会停在wait内部,直到它抢到了锁。

返回阶段

只有当成功重新持有锁后,cv.wait(lock)才会结束阻塞并返回。此时,你的线程恢复了对共享资源的独占访问权限。

信号丢失&虚假唤醒

信号丢失:A发送信号唤醒B,A已经发送信号,但是B还没进入等待,就会倒是B收不到A的信号,这个信号就丢失了。

虚假唤醒:感官上是程序中没有调用notify,唤醒某些处于阻塞的线程。

  • 如何解决

在调用wait前检查条件,生产者只有在队列满的情况下阻塞;消费者在队列空的情况下阻塞;

使用if检查条件可以避免信号丢失。使用while检查变量可以解决信号丢失和虚假唤醒。


为什么if可以防止信号丢失?

信号丢失(Lost Wake-up)发生在:生产者发出了“队列已满”的信号,但消费者此时并没有在等待,或者生产者在消费者还没来得及进入wait状态时就发送notify。

  • 检查条件的必要性:在调用wait()之前检查条件(无论是if还是while),本质上是为了确认当前是否真的需要阻塞。
  • 逻辑:消费者进入临界区后,先看一眼队列。如果队列不为空,它直接拿走数据,根本不调用wait()。这样即使生产者之前发过信号,消费者也已经处理了数据,不会因为错过信号而死锁。

为什么while是金标准?

使用while循环检查条件被称为"Mesa-style monitoring"。它的逻辑是:被唤醒后,必须再次检查条件。

使用while检查状态等效于 cv.wait(unique_lock(mutex),pred)

// 伪代码:cv.wait(lock, pred) 的等效实现 while (!pred()) { wait(lock); }

线程局部存储

thread_local 每一个线程都是独立的副本变量,线程销毁时临时变量销毁。

truct ThreadContext { int thread_id; std::string name; std::vector<int> local_data; ThreadContext() : thread_id(0) { std::cout << "构造线程局部结构体" << std::endl; } ~ThreadContext() { std::cout << "析构线程局部结构体,线程ID: " << thread_id << std::endl; } }; // C++11 thread_local thread_local ThreadContext ctx;

如何调试

gdb命令

## 编译生成 加-g g++ -g test.cpp test -pthread ## 帮助 help /h ## 启动调试 gdb test ## 查看代码 list ## 运行 run /r 运行到第一个断点 start 运行到第一行执行程序 ## 打断点 break / b 行号/函数名 ## 查看所有断点 info b info breakpoints ## 执行 next / n 下一步 不进函数 逐过程 step / s 下一步 进函数 逐语句 continute /c 跳转下一个断点 finish 结束当前函数 info 查看函数局部变量的值 ## 退出 quit /q ## 输出 print / p 变量 p m_vector p m_map p *(m_vector._M_impl._start_)@m_vector.size() display 追踪具体变量值 undisplay 取消追踪 watch 设置观察点 变量修改时打印显示 # x 查看内存 ## 查看所有进程 info thread ## 跳转进程 thread i ## 打印调用独占 bt ## 打印所有线程的调用堆栈 thread apply all bt ## 生成日志文件,开启日志模式 set logging on # 日志功能开启 ## 观察点 watchpoint watch set scheduler-locking on #锁定调度。设置后,当你 next 时,只有当前线程运行,其他线程暂停。防止你在
http://www.jsqmd.com/news/1100793/

相关文章:

  • C# 语言入门(四)闭包、字符串、结构体、枚举、类
  • 告别明文配置风险:构建应用程序敏感数据加密存储与动态解密方案
  • 西门子S7-1200 PLC仿真:用循环移位指令实现8路流水灯,比定时器法省一半代码
  • AI 网关能力再升级!Higress v2.2.3 发布:新增上下文限制与 vLLM 透传支持
  • 企业级多Agent系统实战:从沙盒隔离到动态编排的工程化落地
  • 2026年企业数字化能力地图:从软件定制到AI、云服务、通信、HR与BI如何配置?
  • 绿算亮相中关村丰台园智能经济专场对接会,产融专家联手“破题”
  • 论文党福音:用ChatGPT+Consensus插件,5分钟搞定一个研究方向的参考文献列表
  • 一条液冷板产线要做15种板型:钎焊的“一炉一工艺“为什么接不住多品种订单
  • LangChain 短期记忆 --(Short-term Memory)
  • AutoTask:Android自动化助手终极指南,释放手机潜能
  • 如何用ShaderGlass为Windows桌面添加实时视觉特效:完整实践指南
  • AI-Agent 中 Function-Calling 机制技术报告
  • 叶黄素和花青素哪个对眼睛好?两大热门护眼成分全面对比
  • 从思科课堂到华三机房:H3C交换机基础命令保姆级迁移指南
  • 终极自动化革命:AutoTask如何彻底改变你的手机使用习惯
  • 从RAG到LangGraph:大模型应用开发核心技术与面试实战指南
  • 别再只盯着耦合效率了!用OpticStudio的POP功能,从光束质量M²值重新审视你的单模光纤耦合设计
  • 怎么防止图纸泄密?分享5种方法有效防止图纸泄密,赶紧收藏
  • 青少年视力健康告急!叶黄素能帮什么忙?
  • 解放双手的智能助手:taskt自动化工具深度指南
  • C++11 std::thread 实现
  • Java毕业设计-基于 SpringBoot 的车险寿险业务运维与数据统计系统的设计与实现 基于 SpringBoot 的保险企业业务数据可视化(源码+LW+部署文档+全bao+远程调试+代码讲解等)
  • 别再死磕手册了!手把手教你用Vivado 2023.1搞定7系列FPGA的GTX收发器IP核配置
  • 2026年贵阳本地生活优惠新趋势
  • 告别真机调试!用Unidbg在Windows/Mac上模拟运行Android SO文件(保姆级环境搭建)
  • DX-BT24蓝牙模块保姆级配置指南:从串口调试到手机APP透传,一次搞定
  • 信息化监理在国企信息化建设项目中的关键作用
  • 长期久坐肌肉紧绷?草本外用贴剂日常养护科普
  • 第一章Netty,Selector之Read读事件