デッドロック
分散環境やマルチスレッド環境等、実行コンテキストが複数存在する場合、 共有リソースを取り合って、双方が身動き取れなくなってしまう場合があります。 これをデッドロックといいます。
クリティカルセクションによるデッドロックの解析例
テストプログラムの作成
#include <windows.h> #include <stdio.h> #include <process.h> CRITICAL_SECTION g_cs1; CRITICAL_SECTION g_cs2; unsigned int __stdcall ThreadFunc (void* arg) { EnterCriticalSection( &g_cs2 ); EnterCriticalSection ( &g_cs1 ); printf( "Hello, ThreadFunc!\n" ); EnterCriticalSection ( &g_cs1 ); LeaveCriticalSection ( &g_cs2 ); return 0; } int main(int argc, char* argv[]) { unsigned uThreadId; HANDLE hWorkerThread; InitializeCriticalSection ( &g_cs1 ); InitializeCriticalSection ( &g_cs2 ); // EnterCriticalSection EnterCriticalSection ( &g_cs1 ); // Thread hWorkerThread = (HANDLE) _beginthreadex( NULL, NULL, ThreadFunc, (PVOID) NULL, // param, 0, &uThreadId); if( hWorkerThread ) { CloseHandle(hWorkerThread); } // Sleep ( 1 * 1000 ); EnterCriticalSection ( &g_cs2 ); printf ( "Hello, main!\n" ); LeaveCriticalSection ( &g_cs2 ); LeaveCriticalSection ( &g_cs1 ); DeleteCriticalSection ( &g_cs1 ); DeleteCriticalSection ( &g_cs2 ); return 0; }
makefile は次の通りです。
TARGETNAME=test1 OUTDIR=.\chk LINK32=link.exe ALL : "$(OUTDIR)\$(TARGETNAME).exe" CPPFLAGS=\ /nologo\ /MT\ /W4\ /Fo"$(OUTDIR)\\"\ /Fd"$(OUTDIR)\\"\ /c\ /Zi\ /DWIN32\ /DUNICODE\ /D_UNICODE\ LINK32_FLAGS=\ /nologo\ /subsystem:console\ /pdb:"$(OUTDIR)\$(TARGETNAME).pdb"\ /machine:I386\ /out:"$(OUTDIR)\$(TARGETNAME).exe"\ /DEBUG\ /RELEASE LINK32_OBJS= \ "$(OUTDIR)\$(TARGETNAME).obj" "$(OUTDIR)\$(TARGETNAME).exe" : "$(OUTDIR)" $(LINK32_OBJS) $(LINK32) $(LINK32_FLAGS) $(LINK32_OBJS) "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" .c{$(OUTDIR)}.obj: $(CPP) $(CPPFLAGS) $< .cpp{$(OUTDIR)}.obj: $(CPP) $(CPPFLAGS) $< .cxx{$(OUTDIR)}.obj: $(CPP) $(CPPFLAGS) $<
上記のコードをビルドして、test1.exe を作成します。
test1.exe を実行するとプログラムは何もせず、終了もしません。
解析例
この状態でデバッガをアタッチして、何が起きているかみてみましょう。
0:002> !locks CritSec test1!g_cs1+0 at 0041af00 WaiterWoken No LockCount 1 RecursionCount 1 OwningThread 1538 EntryCount 0 ContentionCount 1 *** Locked CritSec test1!g_cs2+0 at 0041af18 WaiterWoken No LockCount 1 RecursionCount 1 OwningThread 1ba4 EntryCount 0 ContentionCount 1 *** Locked Scanned 37 critical sections 0:002> ~ 0 Id: 1fa0.1538 Suspend: 1 Teb: 7ffde000 Unfrozen 1 Id: 1fa0.1ba4 Suspend: 1 Teb: 7ffdd000 Unfrozen . 2 Id: 1fa0.1964 Suspend: 1 Teb: 7ffdc000 Unfrozen 0:002> ~0 kbn # ChildEBP RetAddr Args to Child 00 0012fe9c 770b9254 770a33b4 0000000c 00000000 ntdll!KiFastSystemCallRet 01 0012fea0 770a33b4 0000000c 00000000 00000000 ntdll!ZwWaitForSingleObject+0xc 02 0012ff04 770a323c 00000000 00000000 7ffdf000 ntdll!RtlpWaitOnCriticalSection+0x155 03 0012ff2c 004010b9 0041af18 00000010 00001ba4 ntdll!RtlEnterCriticalSection+0x152 04 0012ff40 0040157e 00000001 00212320 00212340 test1!main+0x69 05 0012ff88 76d64911 7ffdf000 0012ffd4 7709e4b6 test1!__tmainCRTStartup+0xfb 06 0012ff94 7709e4b6 7ffdf000 7e1b530f 00000000 kernel32!BaseThreadInitThunk+0xe 07 0012ffd4 7709e489 004015d5 7ffdf000 00000000 ntdll!__RtlUserThreadStart+0x23 08 0012ffec 00000000 004015d5 7ffdf000 00000000 ntdll!_RtlUserThreadStart+0x1b 0:002> ~1 kbn # ChildEBP RetAddr Args to Child 00 0031fea8 770b9254 770a33b4 00000010 00000000 ntdll!KiFastSystemCallRet 01 0031feac 770a33b4 00000010 00000000 00000000 ntdll!ZwWaitForSingleObject+0xc 02 0031ff10 770a323c 00000000 00000000 002111f0 ntdll!RtlpWaitOnCriticalSection+0x155 03 0031ff38 00401019 0041af00 0031ff7c 004012ba ntdll!RtlEnterCriticalSection+0x152 04 0031ff44 004012ba 00000000 1d7c2567 00000000 test1!ThreadFunc+0x19 05 0031ff7c 00401362 00000000 0031ff94 76d64911 test1!_callthreadstartex+0x1b 06 0031ff88 76d64911 002111f0 0031ffd4 7709e4b6 test1!_threadstartex+0x82 07 0031ff94 7709e4b6 002111f0 7e38530f 00000000 kernel32!BaseThreadInitThunk+0xe 08 0031ffd4 7709e489 004012e0 002111f0 00000000 ntdll!__RtlUserThreadStart+0x23 09 0031ffec 00000000 004012e0 002111f0 00000000 ntdll!_RtlUserThreadStart+0x1b 0:002>
上記のログから、クリティカルセクション 0041af00 のオーナースレッドは スレッド ID 1538 であることがわかります。 そして、スレッド ID 1538 (=スレッド#0) はそのスタックから、クリティカルセクション 0041af18 を取得するところで停止していることがわかります。
また、クリティカルセクション 0041af18 のオーナースレッドは同様にスレッド #1 であることがわかりますが、 そのスレッドは、クリティカルセクション 0041af00 に入るところで停止しています。
ダンプを採取することによって、デッドロックの状態がこのようにはっきりわかります。