代码如下,3个线程,第一个线程得到锁,Sleep一分钟,后两个线程都等待锁。
class Program
{
static List<int> _list = new List<int>();
static object _syncRoot = new object();
static void Main(string[] args)
{
Thread t = new Thread(F1);
t.Start();
Thread.Sleep(1000);
t = new Thread(F2);
t.Start();
t = new Thread(F3);
t.Start();
Console.ReadKey();
}
static void F1(object o)
{
Console.WriteLine("enter F1");
lock (_syncRoot)
{
Console.WriteLine("F1 enter lock");
_list.Add(1);
Thread.Sleep(1000*60);
}
Console.WriteLine("F1 leave lock");
}
static void F2(object o)
{
Console.WriteLine("enter F2");
lock (_syncRoot)
{
Console.WriteLine("F2 enter lock");
_list.Add(2);
}
Console.WriteLine("F2 leave lock");
}
static void F3(object o)
{
Console.WriteLine("enter F3");
lock (_syncRoot)
{
Console.WriteLine("F3 enter lock");
_list.Add(3);
}
Console.WriteLine("F3 leave lock");
}
}
用windbg按f6附加进程,看同步块信息
0:013> !syncblk
Index SyncBlock MonitorHeld Recursion Owning Thread Info SyncBlock Owner
18 001b623c 5 1 001eae28 164c 12 0143d54c System.Object
-----------------------------
Total 18
CCW 2
RCW 0
ComClassFactory 0
Free 0
12号线程获取了0143d54c资源的锁,有2(MonitorHeld的值减去1,除以2)个线程在等待这个锁。
在.net 1.1里可以直接列出哪些线程在等待这个锁,.net 2.0就不好判断了,这时候可以输入“~* kv”命令,然后搜索“JIT_MonEnterWorker_Portable”和“JITutil_MonContention”,然后看那行的Args列的第一段儿,也就是该方法的第一个参数,如果是!syncblk打出的SyncBlock列的值,那就说明该线程正在等待这个锁,如下
13 Id: 1840.8f4 Suspend: 1 Teb: 7ff3e000 Unfrozen
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
0461f1dc 79f4c88a 00000001 001b6250 00000000 ntdll!KiFastSystemCallRet
0461f244 79f4c4bb 00000001 001b6250 00000000 mscorwks!WaitForMultipleObjectsEx_SO_TOLERANT+0x6f (FPO: [Non-Fpo])
0461f264 79f4c5c4 00000001 001b6250 00000000 mscorwks!Thread::DoAppropriateAptStateWait+0x3c (FPO: [Non-Fpo])
0461f2e8 79f4c659 00000001 001b6250 00000000 mscorwks!Thread::DoAppropriateWaitWorker+0x13c (FPO: [Non-Fpo])
0461f338 79f4c7d9 00000001 001b6250 00000000 mscorwks!Thread::DoAppropriateWait+0x40 (FPO: [Non-Fpo])
0461f394 79e8c54e ffffffff 00000001 00000000 mscorwks!CLREvent::WaitEx+0xf7 (FPO: [Non-Fpo])
0461f3a8 79f649fa ffffffff 00000001 00000000 mscorwks!CLREvent::Wait+0x17 (FPO: [Non-Fpo])
0461f434 79fb874c 001eb4d0 ffffffff 001eb4d0 mscorwks!AwareLock::EnterEpilog+0x8c (FPO: [Non-Fpo])
0461f450 79fb86d0 e937bce2 0461f52c 00000000 mscorwks!AwareLock::Enter+0x61 (FPO: [Non-Fpo])
0461f4f0 00da1481 0143d54c 00000000 00000000 mscorwks!JIT_MonEnterWorker_Portable+0xb3 (FPO: [Non-Fpo])
0461f538 79a00eee 00000000 00000000 00000000 lockTest!lockTest.Program.F2(System.Object)+0x51 (Managed) [D:"huhao_Document"Visual Studio 2005"Projects"ConsoleApplication1"lockTest"Program.cs @ 40]
0461f544 792e019f 00000000 00000000 00000000 mscorlib_ni!System.Threading.ThreadHelper.ThreadStart_Context(System.Object)+0x72a25e (Managed)
0461f558 797db48a 00000000 00000000 00000000 mscorlib_ni!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)+0x6f (Managed)
0461f570 79e71b4c 00000000 00000000 00000000 mscorlib_ni!System.Threading.ThreadHelper.ThreadStart(System.Object)+0x4a (Managed)
0461f580 79e821b1 0461f650 00000000 0461f620 mscorwks!CallDescrWorker+0x33
0461f600 79e96501 0461f650 00000000 0461f620 mscorwks!CallDescrWorkerWithHandler+0xa3 (FPO: [Non-Fpo])
0461f738 79e96534 79253444 0461f894 0461f7c4 mscorwks!MethodDesc::CallDescr+0x19c (FPO: [Non-Fpo])
0461f754 79e96552 79253444 0461f894 0461f7c4 mscorwks!MethodDesc::CallTargetWorker+0x1f (FPO: [Non-Fpo])
0461f76c 79f3d803 0461f7c4 e937b146 001eb4d0 mscorwks!MethodDescCallSite::CallWithValueTypes+0x1a (FPO: [Non-Fpo])
0461f954 79e9845f 0461fad0 00000000 00000000 mscorwks!ThreadNative::KickOffThread_Worker+0x192 (FPO: [Non-Fpo])
14 Id: 1840.14e0 Suspend: 1 Teb: 7ff3d000 Unfrozen
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
0471f0f4 79f4c88a 00000001 001b6250 00000000 ntdll!KiFastSystemCallRet
0471f15c 79f4c4bb 00000001 001b6250 00000000 mscorwks!WaitForMultipleObjectsEx_SO_TOLERANT+0x6f (FPO: [Non-Fpo])
0471f17c 79f4c5c4 00000001 001b6250 00000000 mscorwks!Thread::DoAppropriateAptStateWait+0x3c (FPO: [Non-Fpo])
0471f200 79f4c659 00000001 001b6250 00000000 mscorwks!Thread::DoAppropriateWaitWorker+0x13c (FPO: [Non-Fpo])
0471f250 79f4c7d9 00000001 001b6250 00000000 mscorwks!Thread::DoAppropriateWait+0x40 (FPO: [Non-Fpo])
0471f2ac 79e8c54e ffffffff 00000001 00000000 mscorwks!CLREvent::WaitEx+0xf7 (FPO: [Non-Fpo])
0471f2c0 79f649fa ffffffff 00000001 00000000 mscorwks!CLREvent::Wait+0x17 (FPO: [Non-Fpo])
0471f34c 79fb874c 002040b8 ffffffff 001b623c mscorwks!AwareLock::EnterEpilog+0x8c (FPO: [Non-Fpo])
0471f368 79f64f88 e927bbc2 002040b8 002040b8 mscorwks!AwareLock::Enter+0x61 (FPO: [Non-Fpo])
0471f3d0 79f64d67 ffffffff e927bc62 0471f4ac mscorwks!AwareLock::Contention+0x199 (FPO: [Non-Fpo])
0471f470 00da1549 0143d54c 00000000 00000000 mscorwks!JITutil_MonContention+0xa3 (FPO: [Non-Fpo])
0471f4b8 79a00eee 00000000 00000000 00000000 lockTest!lockTest.Program.F3(System.Object)+0x51 (Managed) [D:"huhao_Document"Visual Studio 2005"Projects"ConsoleApplication1"lockTest"Program.cs @ 50]
0471f4c4 792e019f 00000000 00000000 00000000 mscorlib_ni!System.Threading.ThreadHelper.ThreadStart_Context(System.Object)+0x72a25e (Managed)
0471f4d8 797db48a 00000000 00000000 00000000 mscorlib_ni!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)+0x6f (Managed)
0471f4f0 79e71b4c 00000000 00000000 00000000 mscorlib_ni!System.Threading.ThreadHelper.ThreadStart(System.Object)+0x4a (Managed)
0471f500 79e821b1 0471f5d0 00000000 0471f5a0 mscorwks!CallDescrWorker+0x33
0471f580 79e96501 0471f5d0 00000000 0471f5a0 mscorwks!CallDescrWorkerWithHandler+0xa3 (FPO: [Non-Fpo])
0471f6b8 79e96534 79253444 0471f814 0471f744 mscorwks!MethodDesc::CallDescr+0x19c (FPO: [Non-Fpo])
0471f6d4 79e96552 79253444 0471f814 0471f744 mscorwks!MethodDesc::CallTargetWorker+0x1f (FPO: [Non-Fpo])
0471f6ec 79f3d803 0471f744 e927b0c6 002040b8 mscorwks!MethodDescCallSite::CallWithValueTypes+0x1a (FPO: [Non-Fpo])
以上片段说明13号和14号线程,正在等待12号线程所拥有的锁,在排查死锁问题的时候,如果syncblk打出多行,用上面的方法就可以看到是否有互相等待的资源,然后再想法解决掉。以前用“~*e!dso”也能打出所有线程等待的syncblk object对象,但那样来确定是否是这个线程等待这个锁有些没有根据,只说明这个线程的调用栈上引用了这个同步块儿对象。
参考:http://support.microsoft.com/kb/812614
!SyncBlk in SOS for .NET Framework 2.0
http://dotnetdebug.net/2006/02/23/syncblk-in-sos-for-net-framework-20/comment-page-1/#comment-30066
.NET调试实例-实验1:死锁 - 回顾 (原创翻译)
http://www.cnblogs.com/justinw/archive/2008/07/14/1242970.html
A Hang Scenario, Locks and Critical Sections
http://blogs.msdn.com/tess/archive/2006/01/09/a-hang-scenario-locks-and-critical-sections.aspx
备注:有时候等待所的时候非托管线程并不停在这两个调用栈上,用!u也看不出来和monitor相关的操作,这种情况我还没掌握如何分析,但~*e!dso,然后搜索!syncblk出来的lock object一般可以确定哪些线程等待哪个锁,笨办法,不知道准不准,前提是你尽量用单独的object做来加锁,这些对象保证不做其他用处,这样!dso出来就比较准了。