死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
golang 中的死锁是当 goroutine 被阻塞而没有任何可能被解除阻塞时发生的状态。
死锁可能会导致程序异常,因此如果能够在开发阶段就发现死锁可以大大避免程序的崩溃。
golang 提供了一个死锁检测器,可以帮助开发人员检测出代码中写出的死锁代码。
死锁检测器基于对应用程序创建的线程的进行分析,如果创建和活动的线程数高于等待工作的线程数,则会出现死锁情况。
在检测到死锁时,会创建四个线程:
每次线程空闲时,都会通知检测器。调试的每一行都显示增加的空闲线程数。当空闲线程数等于活动线程数减去系统线程数时,就会发生死锁。
但是,这种行为有一些限制。实际上,任何自旋 goroutine 都会使死锁检测器变得无用,因为线程将保持活动状态。
如果你想可视化运行程序上的死锁,可以使用 pprof 之类的工具来可视化它。
goroutine 会产生死锁,要么是因为它正在等待管道消息,要么 是因为它正在等待同步包中的锁。
当没有其他 goroutine 可以访问通道或锁的时候,一组 goroutine 正在等待对方,但没有一个能够继续,这时就会产生死锁。
目前,Go 只检测整个程序何时冻结,而不检测 goroutine 的子集何时产生死锁。
使用管道通常很容易找出导致死锁的原因。但是大量使用互斥锁的程序却很难调试。
我们知道对于管道的死锁很容易检测,但是对于互斥锁却很难调试发现,因此,有人做出了go-deadlock死锁检测库。
这个死锁检测库并不是基于静态分析的,而是基于运行时检测的。
原理很简单,就是获取当前协程的goroutine id,然后存了当前协程没有释放的lock的对象。 这时候当其他协程去lock的时候,会触发prelock检测,检测有没有冲突lock关系。