首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Windows 10特定的call LeaveCriticalSection崩溃

Windows 10特定的call LeaveCriticalSection崩溃
EN

Stack Overflow用户
提问于 2016-09-22 09:52:57
回答 4查看 2.7K关注 0票数 2

我在Windows 10上遇到了线程同步和关键部分的问题。

在这种情况下,应用程序将崩溃:

  • 应用程序有两个线程。
  • 线程1用对象EnterCriticalSection调用m_CS
  • 线程2然后尝试输入相同的关键部分。
  • 线程1使用TerminateThread终止线程2
  • 线程1调用LeaveCriticalSection

在以前的Windows版本中,我能够测试(7,8,8.1),这是正常工作的。线程2终止,线程1毫无例外地离开关键部分。

在Windows 10上,当Thread 1离开关键部分时,应用程序会因访问冲突而崩溃。只有当另一个线程在等待EnterCriticalThread时终止时才会发生这种情况。

查看堆栈跟踪,它看起来如下(顶部的最新帧):

代码语言:javascript
复制
RtlpWakeByAddress
RtlpUnWaitCriticalSection
RtlLeaveCriticalSection

我花了很多时间来调试这个问题。在我的例子中,当调用m_CS时,LeaveCriticalSection完全没有问题。我调试并花了一些时间来分析ntdll.dll函数的分解代码。在执行RtlpUnWaitCriticalSection的过程中,对象似乎会损坏某个地方,然后在发生崩溃时传递给RtlpWakeByAddress。基本上,ntdll.dll能够修改CRITICAL_SECTION对象的属性,如RtlLeaveCriticalSection中的锁计数。

在网上,我没有找到任何答案,也没有发现Windows 10中有什么变化。只有reddit上的线程和1800次崩溃报告,上个月有相同调用堆栈的Mozilla。我联系了“reddit上的帖子”的作者,到目前为止,他还没能解决这个问题。

因此,有人处理这个问题,并可能有一个解决办法或建议?作为一种解决方案,我现在只看到重新考虑WinAPI TerminateThread的用法,并尽量避免它。另一种方法可能是进行代码重构并考虑应用程序的体系结构。

任何答复都表示感谢。提前感谢

EN

回答 4

Stack Overflow用户

发布于 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可以在任何时间和任何点出现。所以-不调用这个函数,特别是从自我过程。

票数 7
EN

Stack Overflow用户

发布于 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可能导致以下问题:

  • 如果目标线程拥有一个关键部分,则将不会释放关键部分。
  • 如果目标线程从堆中分配内存,则不会释放堆锁。
  • 如果目标线程在终止时正在执行某些kernel32调用,则该线程的进程的kernel32状态可能不一致。
  • 如果目标线程正在操作共享DLL的全局状态,则可能会破坏DLL的状态,从而影响DLL的其他用户。

您应该做的是与线程2通信,让线程2正确和安全地关闭自己。

票数 5
EN

Stack Overflow用户

发布于 2016-09-22 10:15:49

我将更改线程2的代码以使用TryEnterCriticalSection

代码语言:javascript
复制
if(!TryEnterCriticalSection(&m_CS)) {
    return 0;    // Terminate thread
}
//code
LeaveCriticalSection(&m_CS);

这样做的优点是线程2没有等待关键部分,并且它可以正确地终止自己。使用TerminateThread通常是不可取的,正如其他人在评论中已经提到的那样。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39635817

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档