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

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

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

  • 在以前能够测试的Windows版本(7、8、8.1)中,此功能可以正常运行。线程2终止,线程1毫无异常(exception)地离开了关键部分。

    在Windows 10上,当线程1离开关键部分时,应用程序将因访问冲突而崩溃。它仅在等待EnterCriticalThread时终止另一个线程时发生。

    查看堆栈跟踪,它看起来像这样(顶部的最新帧):
    RtlpWakeByAddress
    RtlpUnWaitCriticalSection
    RtlLeaveCriticalSection
    

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

    在网络上,我没有找到任何答案,也没有声明Windows 10中发生了什么变化。在上个月,只有Reddit线程和Mozilla Firefox的〜1800崩溃报告具有相同的调用堆栈。我与reddit上的帖子作者联系,他到目前为止无法解决此问题。

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

    任何回应表示赞赏。
    提前致谢

    最佳答案

    不同版本之间CRITICAL_SECTION的实现非常不稳定。在上一个Windows版本中,线程在CRITICAL_SECTION上开始等待时,他调用 WaitOnAddress 函数。好的,确实是ntdll内部实现-RtlpWaitOnAddress,但这不会改变要点。此函数内部调用RtlpAddWaitBlockToWaitList-此处的关键点-WaitBlock在线程堆栈上分配,并且将此等待块的指针添加到List。然后当CRITICAL_SECTION的所有者离开时,他调用 WakeByAddressSingle (实际上是内部实现RtlpWakeByAddress),此函数从List弹出第一个WaitBlock,从中提取线程ID并调用NtAlertThreadByThreadId(win 8.1中的新api)-唤醒在EnterCriticalSection中等待的线程。但是当您终止线程时,在EnterCriticalSection中等待-他的堆栈被释放。因此WaitBlock块的地址变为无效。因此,当尝试从WaitBlock(死线程堆栈)中读取线程ID时,称为RtlpWakeByAddres s(作为LeaveCriticalSection的一部分)的线程遇到了访问冲突。
    结论-如果您调用TerminatedThread-进程已经变得不稳定,则错误可能随时随地出现。因此-请勿调用此函数,尤其是从自身过程调用时。

    10-05 20:50
    查看更多