Windbg调试互斥体(Mutex)死锁
一. 测试代码
#include <windows.h>
#include <tchar.h>
#include <process.h>
HANDLE hMutexA = NULL;
HANDLE hMutexB = NULL;
unsigned __stdcall ThreadProc1(void * pArg) {
WaitForSingleObject(hMutexA, INFINITE);
Sleep(500);
WaitForSingleObject(hMutexB, INFINITE);
printf("+++\n");
ReleaseMutex(hMutexB);
ReleaseMutex(hMutexA);
return 0;
}
unsigned __stdcall ThreadProc2(void * pArg) {
WaitForSingleObject(hMutexB, INFINITE);
Sleep(500);
WaitForSingleObject(hMutexA, INFINITE);
printf("...\n");
ReleaseMutex(hMutexA);
ReleaseMutex(hMutexB);
return 0;
}
int main()
{
hMutexA = CreateMutex(NULL, FALSE, TEXT("MutexA"));
hMutexB = CreateMutex(NULL, FALSE, TEXT("MutexB"));
// 启动线程
HANDLE hThread1 = (HANDLE)_beginthreadex(NULL, 0, ThreadProc1, NULL, 0, NULL);
HANDLE hThread2 = (HANDLE)_beginthreadex(NULL, 0, ThreadProc2, NULL, 0, NULL);
getchar();
// 等待线程退出并关闭句柄
if (hThread1) {
WaitForSingleObject(hThread1, INFINITE);
CloseHandle(hThread1);
}
if (hThread2) {
WaitForSingleObject(hThread2, INFINITE);
CloseHandle(hThread2);
}
// 关闭句柄
if(hMutexA)
CloseHandle(hMutexA);
if(hMutexB)
CloseHandle(hMutexB);
return 0;
}
二. 死锁原理
程序生成了2个线程(线程1、线程2)和2个互斥体MutexA和MutexB。
观察线程执行代码可知,这是一个典型的死锁用例,2个线程相互等待。
线程1: 拥有MutexA --> 过一段时间(sleep) ---> 想拥有MutexB
线程2: 拥有MutexB --> 过一段时间(sleep) ---> 想拥有MutexA
线程1想拥有属于线程2的MutexB,而线程2却想拥有属于线程1的MutexA,互不松手,就只能都等着了。
三. 上调试器
~*kvn
查看所有线程调用堆栈:
从线程#1栈帧03可以看到其正在等待句柄00000038。
从线程#2栈帧03可以看到其正在等待句柄00000034。
即:
线程#1(ID:22f4)--->等待句柄38
线程#2(ID:33bc)---> 等待句柄34
到底句柄00000034和00000038是什么类型的,可以使用!handle
命令查看:
从图中可以看到:
句柄00000034为名为MutexA的互斥体,被线程ID:2264 拥有。
句柄00000038为名为MutexB的互斥体,被线程ID:33bc 拥有。
即:
线程#1(ID:22f4)等待00000038(互斥体MutexA ),拥有00000034(互斥体MutexB)
线程#2(ID:33bc)等待句柄00000034(互斥体MutexB ),拥有00000038(互斥体MutexA)
所以,爱就是放手,有时候放手会让彼此过得更好。