1 先上死锁代码 如下:
#include "stdafx.h"
#include <mutex>
#include <thread>
#include <windows.h>
// windows系统中 std::mutex内部是通过Event内核对象实现的,而不是CRITICAL_SECTION
std::mutex mtx0;
std::mutex mtx1;
CRITICAL_SECTION cs0;
CRITICAL_SECTION cs1;
int _tmain(int argc, _TCHAR* argv[])
{
InitializeCriticalSectionAndSpinCount(&cs0, 4000);
InitializeCriticalSectionAndSpinCount(&cs1, 4000);
std::thread thread0([]{
EnterCriticalSection(&cs0);
std::this_thread::sleep_for(std::chrono::seconds(3));
EnterCriticalSection(&cs1);
});
std::thread thread1([]{
EnterCriticalSection(&cs1);
std::this_thread::sleep_for(std::chrono::seconds(3));
EnterCriticalSection(&cs0);
});
printf("enter dead lock...");
thread0.join(); // 主线程将永远阻塞在这里,因为thread0 和 thread1之间发生了死锁
thread1.join();
printf("leave dead lock...");
return 0;
}
2 release下,去掉pdb,编译出exe,运行如下:
3 将windbg附加到此进程中:
4 查看主线程堆栈: ~0kb
~0kb 命令简介: 0是主线程的序号 kb表示输出调用堆栈中函数的前3个参数;(~波浪线就像一个线头thread,所以线程相关命令都由~开头) 查看线程堆栈的详细命令参见:https://blog.csdn.net/luchengbiao/article/details/89240601
通过堆栈可知 主线程在等待(WaitForSingleObject )一个内核对象受信,故而阻塞!!!
5 查看句柄信息: !handle 0000014c f
易知 函数[Nt] WaitForSingleObject [Ex] 是在阻塞等待一个内核对象受信,其中第一个参数值就是要相应的内核对象句柄,此处就是0x0000014c;
所以通过命令 !handle 0000014c f 命令查看内核对象句柄的全部信息(full information) (查看内核对象句柄的信息的详细命令参见https://blog.csdn.net/luchengbiao/article/details/89241054):
可知 此内核对象是一个线程,线程id是 13c4.22b4 其中13c4表示所属进程id 22b4表示线程id
6 查找到相应线程: ~*kb
~*kb 命令简介: *通配符表示所有线程 kb表示输出调用堆栈中函数的前3个参数;查看线程堆栈的详细命令参见:https://blog.csdn.net/luchengbiao/article/details/89240601
也可以通过线程id直接查找相应线程,~~[0x22b4], 参见step8
可见 线程id是13c4.22b4 的是线程1,透过其堆栈又可知 线程1阻塞在一个CRITIAL_SECTION关键段上
7 查看关键段CRITIAL_SECTION: !cs 00073470
!cs 00073470 命令简介:查看地址是00073470的CRITIAL_SECTION对象的信息,详细命令参见https://blog.csdn.net/luchengbiao/article/details/89241054
LOCKED: 表示此cs已被加锁
OwingThread:表示锁定此cs的线程id
RecursionCount:表示重入次数; 何为重入: 在同一个线程中可以多次EnterCriticalSection同一个cs,并记录重入的次数;C++11中的std::recursive_mutex类此;
SpinCount:表示此cs的自旋次数,可在 InitializeCriticalSectionAndSpinCount(&cs0, 4000)的第二个参数指定 0x0fa0 == 4000; 自旋是一种busy wait机制,相应的还有sleep wait:先最多尝试4000次 busy,如果还不能成功EnterCritialSection(即own the cs)则才进入sleep wait,陷入内核等待唤醒,内部由Event相应实现,参见windows核心编程。
可知此cs已被id为0x000024c8的线程加锁
8 再次查找线程:
~~[0x000024c8] 通过线程id查找相应线程
~2kb
可见 线程id是24c8 的是线程2,通过其堆栈又可知 线程2阻塞在一个CRITIAL_SECTION关键段上
9 再次查看关键段CRITIAL_SECTION: !cs 00073488
10 再次查找线程:
~~[000022b4]