这是一个手动转储。有些人声称存在僵局。
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
答案 0 :(得分:2)
对于1):该线程可能已被异常终止或正常结束而有人忘记实施LeaveCriticalSection
。
2):
当其他线程调用EntryCount
EnterCriticalSection()
会递增
EntryCount和ContentionCount永远不会减少
对于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;
}