Windows 10特定的崩溃调用leaveccriticalsection
Windows 10 specific crash on call LeaveCriticalSection
我遇到了Windows 10的线程同步和临界区问题。
在这种情况下应用程序将崩溃:
- 应用程序有两个线程 线程1调用EnterCriticalSection对象m_CS
- 线程2试图进入相同的临界区
- 线程1使用TerminateThread终止线程2
- 线程1调用leaveccriticalsection
在我能够测试的以前的Windows版本(7,8,8.1)中,这可以正常工作。线程2终止,线程1离开临界区,无一例外。
在Windows 10上,当线程1离开临界区时,应用程序会因访问冲突而崩溃。只有当另一个线程在等待EnterCriticalThread时被终止时才会发生。
看堆栈跟踪,它看起来是这样的(最新帧在顶部):
RtlpWakeByAddress
RtlpUnWaitCriticalSection
RtlLeaveCriticalSection
我花了很多时间来调试这个问题。在我的情况下,m_CS是完全好的,当leaveccriticalsection被调用。我调试并花了一些时间分析ntdll.dll函数的反汇编代码。似乎对象在执行RtlpUnWaitCriticalSection期间损坏了某个地方,然后在崩溃发生时传递给RtlpWakeByAddress。基本上,ntdll.dll能够修改CRITICAL_SECTION对象的属性,例如rtlleaveccriticalsection中的锁计数。
在网上我没有找到任何关于这个问题的答案,也没有找到关于Windows 10发生了什么变化的说法。上个月只有reddit上的帖子和1800个Mozilla Firefox的崩溃报告使用了相同的调用栈。我联系了reddit上帖子的作者,他到目前为止还不能解决这个问题。
所以有人处理这个问题,可能有一个解决方案或建议?作为一个解决方案,现在我只看到重新考虑WinAPI TerminateThread的使用,并尽量避免它。另一种方法可能是进行代码重构并考虑应用程序的体系结构。
感谢任何回应。提前感谢
CRITICAL_SECTION
的实现在版本之间非常不稳定。当在最后一个Windows版本线程开始等待CRITICAL_SECTION
他调用WaitOnAddress
函数。好的,它实际上是内部实现- RtlpWaitOnAddress
,但这不是更改要点。这个函数内部调用RtlpAddWaitBlockToWaitList
——这里是关键点——WaitBlock被分配到线程堆栈上,指向这个等待块的指针被添加到List中。然后当CRITICAL_SECTION
的所有者离开时,他调用WakeByAddressSingle
(实际上是内部实现RtlpWakeByAddress
),这个函数从列表中弹出第一个WaitBlock,从中提取线程Id并调用NtAlertThreadByThreadId
(win 8.1的新api) -唤醒EnterCriticalSection
中等待的一些线程。但是当你终止线程,等待在EnterCriticalSection
-他的堆栈被释放。因此,WaitBlock块的地址无效。因此,当尝试从WaitBlock(死线程堆栈)读取线程Id时,调用RtlpWakeByAddres
s的线程(作为LeaveCriticalSection
的一部分)获得了访问冲突。结论-如果你调用TerminatedThread
-进程已经变成不稳定状态,bug可以在任何时间和任何点出现。不要调用这个函数,尤其是在self进程中。
线程1使用TerminateThread终止线程2
不要那样做。它可能看起来像它在其他windows版本上工作,但你没有办法知道确切的副作用正在发生和隐藏你。
从https://msdn.microsoft.com/en-us/library/windows/desktop/ms686717 (v = vs.85) . aspx
TerminateThread是一个危险的函数,应该只在最极端的例子。您应该只在以下情况下调用TerminateThread确切地知道目标线程在做什么,就可以控制所有的线程目标线程当时可能正在运行的代码终止。例如,TerminateThread会导致以下问题:
- 如果目标线程拥有临界区,则该临界区不会被释放。
- 如果目标线程正在从堆中分配内存,堆锁将不会被释放。
如果目标线程在终止时正在执行某些kernel32调用,则该线程进程的kernel32状态可能为不一致。如果目标线程正在操作共享DLL的全局状态,那么DLL的状态可能会被破坏,从而影响到其他用户DLL。
你应该做的是与线程2通信,让线程2正确安全地关闭自己。
我会将线程2的代码改为使用TryEnterCriticalSection
if(!TryEnterCriticalSection(&m_CS)) {
return 0; // Terminate thread
}
//code
LeaveCriticalSection(&m_CS);
这样做的好处是线程2没有等待临界区,它可以正确地终止自己。一般不建议使用TerminateThread
,正如其他人在评论中已经提到的那样。
是的,我可以确认这种行为,并花了3天多的时间在我们的代码中找到内存泄漏,这破坏了我的CRITICAL_SECTION
。问题是一个旧的TerminateThread调用。这个程序工作得很好,但现在在Windows10上,我们显然在EnterCriticalSection
或LeaveCriticalSection
中发生了访问违规。非常感谢你,这让我很开心。
- 将方法转换为调用该方法的成员函子对象会导致崩溃
- 在从Qt调用的Python脚本中导入OpenCV崩溃
- 调用 free() 有时会导致程序崩溃
- 调用 java 的回调() 时应用程序崩溃.由于 detatchThread 而获得运行时错误
- Linux c++.在预加载的共享库中定义的基类的崩溃调用函数
- 使用 MS 绕道挂钩在调用钩子函数时崩溃C++
- 矢量迭代器在尝试调用函数时使我的程序崩溃
- 使用唯一指针调用函数会使我的程序崩溃
- 钩/绕道 d3d9 (现在/结束场景) - 似乎调用我的函数然后崩溃
- 亲.InsertAtHead() 调用创建程序崩溃
- 重写require后调用"lua_getfield()"时崩溃
- 当从成员类调用封装的std::begin时,程序崩溃
- Shaderc在android上调用glsl到spv程序集时崩溃
- 从 C# 模块调用C++函数引发随机崩溃
- 从 C# Windows 应用程序调用 C dll 会导致 svchost.exe 崩溃
- EGL 在第一次 opengl 函数调用时崩溃
- 如果调用 RtlSetProcessIsCritical,将使用 lstrcmpW 将命令行与值进行比较将使程序崩溃
- C++如果调用 vector,矢量分割错误会导致崩溃
- 由于调用 std::condition_variable 后参数无效而导致应用程序崩溃
- Windows 10特定的崩溃调用leaveccriticalsection