Windows 7下静态变量析构调用std::conditional_variable notify()挂起问题求助
std::condition_variable::notify_one()导致程序挂起的问题分析与解决 问题场景与复现细节
你碰到的是一个Windows 7平台特有的STL运行时问题:当静态SomeObject对象在析构阶段调用StopThreadAndWait()时,其中的m_procesTasks.notify_one()会直接导致程序挂起,而同样的代码在Windows 10下运行完全正常。
你的简化代码逻辑如下:
class SomeObject { public: ~SomeObject() { StopThreadAndWait(); } void StopThreadAndWait() { /* 一些逻辑 */ m_stop = true; m_procesTasks.notify_one(); // <- 问题触发点 if (m_thread.joinable()) m_thread.join(); } private: bool m_stop; std::mutex m_workQueueSync; std::thread m_thread; std::condition_variable m_procesTasks; };
挂起时的调用栈明确指向Windows 7底层的条件变量实现:
tdll.dll!ZwReleaseKeyedEvent() ntdll.dll!RtlpWakeConditionVariable() ntdll.dll!RtlWakeConditionVariable() MSVCP140D.dll!__crtWakeAllConditionVariable(_RTL_CONDITION_VARIABLE * pCond) MSVCP140D.dll!Concurrency::details::stl_condition_variable_win7::notify_one() .... tdll.dll!RtlExitUserProcess() ...
问题根源解析
正如你指出的,静态对象析构中做同步本身就是不良实践,但这个问题的核心是Windows 7下MSVCP140运行时的std::condition_variable实现bug:当等待该条件变量的线程已经因进程关闭等原因提前终止时,调用notify_one()/notify_all()会触发底层系统调用的无响应挂起。
这个问题在Windows 10的系统内核和STL运行时中已经被修复,因此不会出现类似异常。
可行解决方案
结合遗留代码的实际情况,这里提供两种落地思路:
最佳方案:重构架构
静态对象绑定工作线程本身就违背了可控生命周期的设计原则,静态对象的析构时机完全由进程退出逻辑决定,很容易引发线程同步冲突。建议重构代码,将SomeObject改为动态管理的对象,在程序退出前主动调用停止线程的接口,确保线程完全终止后再销毁对象,从根源上避免这类问题。替代方案:切换为Boost库实现
如果暂时无法重构遗留代码,可以直接用Boost库的boost::condition_variable替换STL版本。Boost的条件变量实现没有这个Windows 7特有的兼容性问题,能兼容现有代码逻辑,快速解决挂起问题。
内容的提问来源于stack exchange,提问作者Serhii Pavlenko

