项目中在用户机器Win7系统上好几次出现进程卡死,无法退出,在用户机器上抓取了dump,发现是在DllMain函数中执行了静态变量的析构,这个静态变量析构的时候会使用std::condition_variable 类型的成员变量通知其他线程退出。同时本地在win10机器上并不会出现。
最开始一直没查到原因,解决方法是在main函数退出之前就把静态变量反初始化函数执行,这样在DllMain中就只是一个空的析构函数,不会导致卡死了。
卡死dump截图:
当时我给的建议是,尽量不要在DllMain中执行很多同步逻辑,因为官方的最佳实践文档中给了出的建议也是这样:
https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices
第三条很明确的说了,不要和其他线程做同步的事情。
(备注:第一条曾经也遇到过这类死锁的dump,调用com组件的时候有时候会加载动态库,从而导致死锁。关键字:Loader Lock)
一年之后又遇到了一个无法退出的case,我给用户远程并抓取了dump,发现还是类似的问题,只不过代码是在另外一个地方。
上次是我同事发现的这个问题,想办法绕过去了,这次我想弄明白原因,在网上查了一些资料,google查找的关键字:ZwReleaseKeyedEvent deadlock,因为堆栈最后一个函数是这个。
发现了很有价值的两篇文章:
第一篇:
这篇文章这个人也遇到了,代码和我们的代码几乎一样:
class SomeObject
{
public:
~SomeObject()
{
StopThreadAndWait();
}
void StopThreadAndWait()
{
/* some logic */
m_stop = true;
m_procesTasks.notify_one(); // <- the problem is here
if (m_thread.joinable())
m_thread.join();
}
private:
...
bool m_stop;
std::mutex m_workQueueSync;
std::thread m_thread;
std::condition_variable m_procesTasks;
};
第二篇:
https://bugzilla.mozilla.org/show_bug.cgi?id=970063
也就是说mozilla也遇到过这个问题,并且还给出了问题的答案,答案之一就是安装补丁,因为这个是windows系统的bug:
补丁链接:
A process that is being terminated stops responding in Windows 7 or in Windows Server 2008 R2 - Microsoft Support
上面说明了只在win7 和 Windows Server 2008 R2 系统上会出现此问题。
到此,整个问题已经查明原因了。
但是,用户那里环境我们几乎是不能改变的,比如给用户安装这个补丁似乎成本很大。所以我们 就修改我们自己的代码,不要在DllMain中因为析构静态变量,执行了 std::condition_variable::notify_one(all)之类的函数,放到main函数退出之前执行即可。
第一篇文章中使用boost库中的condition_variable类也可以解决。同时我也把我发现的这个问题写在了这篇文章的评论中了。