我在Windows 10上遇到了线程同步和关键部分的问题。
在这种情况下,应用程序将崩溃:
在以前的Windows版本中,我能够测试(7,8,8.1),这是正常工作的。线程2终止,线程1毫无例外地离开关键部分。
在Windows 10上,当Thread 1离开关键部分时,应用程序会因访问冲突而崩溃。只有当另一个线程在等待EnterCriticalThread时终止时才会发生这种情况。
查看堆栈跟踪,它看起来如下(顶部的最新帧):
RtlpWakeByAddress
RtlpUnWaitCriticalSection
RtlLeaveCriticalSection我花了很多时间来调试这个问题。在我的例子中,当调用m_CS时,LeaveCriticalSection完全没有问题。我调试并花了一些时间来分析ntdll.dll函数的分解代码。在执行RtlpUnWaitCriticalSection的过程中,对象似乎会损坏某个地方,然后在发生崩溃时传递给RtlpWakeByAddress。基本上,ntdll.dll能够修改CRITICAL_SECTION对象的属性,如RtlLeaveCriticalSection中的锁计数。
在网上,我没有找到任何答案,也没有发现Windows 10中有什么变化。只有reddit上的线程和1800次崩溃报告,上个月有相同调用堆栈的Mozilla。我联系了“reddit上的帖子”的作者,到目前为止,他还没能解决这个问题。
因此,有人处理这个问题,并可能有一个解决办法或建议?作为一种解决方案,我现在只看到重新考虑WinAPI TerminateThread的用法,并尽量避免它。另一种方法可能是进行代码重构并考虑应用程序的体系结构。
任何答复都表示感谢。提前感谢
发布于 2016-09-22 10:39:53
CRITICAL_SECTION的实现从一个版本到另一个版本非常不稳定。在上一次Windows线程开始等待CRITICAL_SECTION时,他调用WaitOnAddress函数。好的,实际上这是所有内部实现- RtlpWaitOnAddress,但这并不改变要点。这个函数内部调用RtlpAddWaitBlockToWaitList -在这里,在线程堆栈上分配关键点- WaitBlock,并将指向这个等待块的指针添加到List中。然后,当CRITICAL_SECTION的所有者离开时,他调用WakeByAddressSingle (实际上是内部实现RtlpWakeByAddress),这个函数从列表中弹出第一个WaitBlock,从其中提取线程Id并调用NtAlertThreadByThreadId(新api来自win 8.1) --以唤醒在EnterCriticalSection中等待的线程。但是当您终止线程时,在EnterCriticalSection中等待--他的堆栈被解除分配。因此,WaitBlock块的地址变得无效。因此,调用RtlpWakeByAddress (作为LeaveCriticalSection的一部分)的线程在尝试从WaitBlock (死线程堆栈)读取线程Id时受到访问冲突。结论--如果您调用TerminatedThread -进程已经处于不稳定状态,bug可以在任何时间和任何点出现。所以-不调用这个函数,特别是从自我过程。
发布于 2016-09-22 15:39:45
线程1使用TerminateThread终止线程2
别干那事。它可能看起来像它在其他窗口版本上工作,但你没有办法确切地知道什么副作用正在发生和隐藏你。
来自https://msdn.microsoft.com/en-us/library/windows/desktop/ms686717(v=vs.85).aspx
TerminateThread是一个危险的函数,只能在最极端的情况下使用。只有当您确切地知道目标线程正在执行什么,并且控制目标线程可能在终止时运行的所有代码时,才应该调用TerminateThread。例如,TerminateThread可能导致以下问题:
您应该做的是与线程2通信,让线程2正确和安全地关闭自己。
发布于 2016-09-22 10:15:49
我将更改线程2的代码以使用TryEnterCriticalSection。
if(!TryEnterCriticalSection(&m_CS)) {
return 0; // Terminate thread
}
//code
LeaveCriticalSection(&m_CS);这样做的优点是线程2没有等待关键部分,并且它可以正确地终止自己。使用TerminateThread通常是不可取的,正如其他人在评论中已经提到的那样。
https://stackoverflow.com/questions/39635817
复制相似问题