CriticalSection Count-Variables不一致?

时间:2017-12-13 13:07:44

标签: deadlock windbg crash-dumps critical-section locks

这是一个手动转储。有些人声称存在僵局。

1)!lock表示我的关键部分的OwningThread是19a0,但该线程甚至不存在? (看看〜输出)

2)EntryCount为0,因此唯一调用EnterCriticalSection的线程是19a0。 ContentionCount是cd(205),这意味着其他线程确实尝试进入EnterCriticalSection?这两件事是不矛盾的吗?

3)RecursionCount为1,意味着线程19a0初始化调用EnterCriticalSection,导致LockCount:0和RecursionCount:1。如果RecursionCount仍为1,LockCount如何达到8?它必须是8和9,因为我知道EntryCount是0,这意味着只有线程19a0调用了EnterCriticalSection那8次?

4)在他们的kb输出中显示8个不同的线程:

02 00007ff8`500af83f : 00000146`**ed3502a0** 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlpWaitOnAddress+0xb2. 

那么EntryCount怎么能在这里0? WaitOnAdress很重要吗?或者只有EnterCriticalSection / EnterCriticalSectionContended?

5)对这个关键部分可能发生的事情的一般解释会有所帮助。

0:021> !locks
CritSec +ed3502a0 at 00000146ed3502a0
WaiterWoken        No
LockCount          8
RecursionCount     1
OwningThread       19a0
EntryCount         0
ContentionCount    cd
*** Locked

0:021> ~
#  0  Id: 10f40.ca10 Suspend: 0 Teb: 00000031`d9312000 Unfrozen
1  Id: 10f40.bcc4 Suspend: 0 Teb: 00000031`d931a000 Unfrozen
2  Id: 10f40.4de4 Suspend: 0 Teb: 00000031`d931c000 Unfrozen
3  Id: 10f40.4b9c Suspend: 0 Teb: 00000031`d931e000 Unfrozen
4  Id: 10f40.b34 Suspend: 0 Teb: 00000031`d9320000 Unfrozen
5  Id: 10f40.12680 Suspend: 0 Teb: 00000031`d9328000 Unfrozen
6  Id: 10f40.f420 Suspend: 0 Teb: 00000031`d932a000 Unfrozen
7  Id: 10f40.7d5c Suspend: 0 Teb: 00000031`d932c000 Unfrozen
8  Id: 10f40.f544 Suspend: 0 Teb: 00000031`d932e000 Unfrozen
9  Id: 10f40.7774 Suspend: 0 Teb: 00000031`d9330000 Unfrozen
10  Id: 10f40.101a0 Suspend: 0 Teb: 00000031`d9332000 Unfrozen
11  Id: 10f40.104e8 Suspend: 0 Teb: 00000031`d9336000 Unfrozen
12  Id: 10f40.135e8 Suspend: 0 Teb: 00000031`d9338000 Unfrozen
13  Id: 10f40.7ad0 Suspend: 0 Teb: 00000031`d9342000 Unfrozen
14  Id: 10f40.113ec Suspend: 0 Teb: 00000031`d9344000 Unfrozen
15  Id: 10f40.7a7c Suspend: 0 Teb: 00000031`d9346000 Unfrozen
16  Id: 10f40.6b18 Suspend: 0 Teb: 00000031`d9354000 Unfrozen
17  Id: 10f40.a414 Suspend: 0 Teb: 00000031`d9356000 Unfrozen
18  Id: 10f40.133b4 Suspend: 0 Teb: 00000031`d935a000 Unfrozen
19  Id: 10f40.11794 Suspend: 0 Teb: 00000031`d935c000 Unfrozen
20  Id: 10f40.4e40 Suspend: 0 Teb: 00000031`d935e000 Unfrozen
. 21  Id: 10f40.8b5c Suspend: 0 Teb: 00000031`d9360000 Unfrozen
22  Id: 10f40.115b4 Suspend: 0 Teb: 00000031`d9362000 Unfrozen
23  Id: 10f40.1f38 Suspend: 0 Teb: 00000031`d9364000 Unfrozen
24  Id: 10f40.e560 Suspend: 0 Teb: 00000031`d9368000 Unfrozen
25  Id: 10f40.1047c Suspend: 0 Teb: 00000031`d92c2000 Unfrozen
26  Id: 10f40.ad40 Suspend: 0 Teb: 00000031`d9214000 Unfrozen
27  Id: 10f40.8e00 Suspend: 0 Teb: 00000031`d9208000 Unfrozen
28  Id: 10f40.af38 Suspend: 0 Teb: 00000031`d9220000 Unfrozen
29  Id: 10f40.c6a4 Suspend: 0 Teb: 00000031`d9222000 Unfrozen
30  Id: 10f40.14114 Suspend: 0 Teb: 00000031`d9224000 Unfrozen

1 个答案:

答案 0 :(得分:2)

对于1):该线程可能已被异常终止或正常结束而有人忘记实施LeaveCriticalSection

2): 当其他线程调用EntryCount

时,EnterCriticalSection()会递增
  

EntryCount和ContentionCount永远不会减少

documented

对于3):

字段LockCount不再是真正的锁定计数,如中所述 this answer。相关部分:

  

在Microsoft Windows Server 2003 Service Pack 1和更高版本的Windows中,LockCount字段的解析方式如下:

     
      
  • 最低位显示锁定状态。如果该位为0,则临界区被锁定;如果为1,则关键部分未锁定。
  •   
  • 下一位显示是否已为此锁定唤醒线程。如果该位为0,则该锁已被唤醒;如果是1,则没有线程被唤醒。
  •   
  • 其余位是等待锁定的线程数的补码。
  •   

对于4):RtlpWaitOnAddress恕我直言不是那么有用。调用堆栈上也应该有一些RtlpEnterCriticalSection。那里的参数可以用于!cs命令。

For 5):我的一个演示是以下代码。它使用事件而不是临界区,但结果是相同的:

#include "stdafx.h"
#include <Windows.h>
#include <process.h>
#include <stdio.h>
#include <iostream>


HANDLE threadA;
HANDLE threadB;
HANDLE eventB;

class WorkItem
{
public:
    virtual void Initialize()
    {
    }
};

unsigned int __stdcall initializeWorkitems(void* param) 
{
    try
    {
        // Initialize workitems
        WorkItem **items = new WorkItem*[2];
        items[0] = new WorkItem();
        items[1] = NULL;

        for (int i = 0; i<2; i++)
        {
                items[i]->Initialize();
        }

        // Signal event for second thread to work on work items
        SetEvent(eventB);
    }
    catch(...)
    {
        // Don't do this
    }
    return 0;
}

unsigned int __stdcall processWorkitems(void* param) 
{
    // Wait for work item initialization to complete
    WaitForSingleObject(eventB, INFINITE);

    // Work on workitems
    Sleep(100);

    return 0;
}


int _tmain(int argc, _TCHAR* argv[])
{
    eventB = CreateEvent(0, 0, 0, 0);

    threadA = (HANDLE)_beginthreadex(0, 0, &initializeWorkitems, (void*)0, 0, 0);
    threadB = (HANDLE)_beginthreadex(0, 0, &processWorkitems, (void*)0, 0, 0);

    WaitForSingleObject(threadA, INFINITE);
    WaitForSingleObject(threadB, INFINITE);

    CloseHandle(threadA);
    CloseHandle(threadB);

    CloseHandle(eventB);

    return 0;
}