【问题标题】:Debugging a deadlock with ReaderWriterLockSlim使用 ReaderWriterLockSlim 调试死锁
【发布时间】:2014-09-14 13:14:20
【问题描述】:

我正在调试面向 .NET 3.5 的托管应用程序中的挂起。其中一个线程一直在等待方法System.Threading.ReaderWriterLockSlim.EnterWriteLock。为了找出哪个线程拥有锁,我检查了ReaderWriterLockSlim 类的internal member field ReaderWriterCount[] rwc 以及它包含的所有ReaderWriterCountRecursiveCounts 对象。这些对象包含有关所有线程的信息。所有对象都包含writercount=0readercount=0,除了包含readercount=1 的单个对象:

[53] 0144fc84
    Name: System.Threading.ReaderWriterCount
    MethodTable: 6bb4e930
    EEClass: 6b9ba4d0
    Size: 24(0x18) bytes
     (C:\Windows\assembly\GAC_MSIL\System.Core\3.5.0.0__b77a5c561934e089\System.Core.dll)
    Fields:
          MT    Field   Offset                 Type VT     Attr    Value Name
    55782f94  4000625        c         System.Int32  1 instance       53 threadid
    55782f94  4000626       10         System.Int32  1 instance        1 readercount
    6bb4e930  4000627        4 ...ReaderWriterCount  0 instance 00000000 next
    6bb4e858  4000628        8 ...g.RecursiveCounts  0 instance 0144fc9c rc

AFAIU 表示 MTID 为 53 的线程拥有锁。我用 kb 倾倒了它的堆栈(!clrstack 失败,因为它不是托管线程)并得到以下信息:

ChildEBP RetAddr  Args to Child              
16eee9b4 765c14ab 00000280 00000000 16eee9fc ntdll!ZwWaitForSingleObject+0x15
16eeea20 778d1194 00000280 00009c40 00000000 KERNELBASE!WaitForSingleObjectEx+0x98
16eeea38 681954d7 00000280 00009c40 00000000 KERNEL32!WaitForSingleObjectExImplementation+0x75
16eeea7c 68195423 00000280 00009c40 00000000 mscorwks!PEImage::LoadImage+0x1af
16eeeacc 68195442 00009c40 00000000 00000000 mscorwks!CLREvent::WaitEx+0x117
16eeeae0 681d95c7 00009c40 00000000 00000000 mscorwks!CLREvent::Wait+0x17
16eeeb60 681d9a55 03376058 00009c40 00000000 mscorwks!ThreadpoolMgr::SafeWait+0x73
16eeebc4 68226508 00000000 00000000 00000000 mscorwks!ThreadpoolMgr::WorkerThreadStart+0x11c
16eefa64 778d338a 04b2e5c8 16eefab0 77e09f72 mscorwks!Thread::intermediateThreadProc+0x49
16eefa70 77e09f72 04b2e5c8 6aecf560 00000000 KERNEL32!BaseThreadInitThunk+0xe
16eefab0 77e09f45 682264c2 04b2e5c8 00000000 ntdll!__RtlUserThreadStart+0x70
16eefac8 00000000 682264c2 04b2e5c8 00000000 ntdll!_RtlUserThreadStart+0x1b

这意味着它是一个Idle CLR Worker Thread

我的第一个想法是当阅读器锁并不总是被释放时,应用程序代码中存在错误。但是这个假设没有得到证实,因为代码在 ReaderWriterLockSlim.EnterWriteLock 上使用了包装器,看起来像这样:

readerWriterLockSlim.EnterReadLock();

try
{
    return executeFunc();
}
finally
{
    readerWriterLockSlim.ExitReadLock();
}

finally 块必须保证锁总是被释放。

任何想法这种情况是怎么可能的?在获取锁之后但在finally 之前,线程是否会以某种方式中止,然后成为空闲线程池线程?可能是如何缩小问题的提示?

附:这个死锁只复制了一次,我只有一个内存转储,因此我不能轻易地说在锁周围添加跟踪或断点并对此进行试验。

【问题讨论】:

  • @usr 我的实验表明,在重入的情况下,readercount 将等于 2 或更大...
  • 并且锁是递归的。

标签: c# .net multithreading debugging windbg


【解决方案1】:

试试 !sosex.dlk 和/或 !mlocks 和 !mwaits。这应该会告诉你发生了什么。

【讨论】:

  • 感谢您的提示。但是这些命令以更简单的方式给了我完全相同的信息。一个空闲的线程池线程持有一个读锁并阻塞以继续另一个。
  • 对不起。我已经更仔细地阅读了您的帖子,这看起来确实很奇怪。您确定您发布的代码 sn-p 是唯一对该锁进行读取锁定的代码吗?如果是这样,那么我无法解释这种行为。
【解决方案2】:

可能是您遇到了跳过 finally 代码块的异步异常(如线程中止)。 您可以在 一些限制:可靠性部分下找到更多关于它的信息here

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-30
    • 1970-01-01
    相关资源
    最近更新 更多