七叶笔记 » golang编程 » golang中的死锁

golang中的死锁

什么是死锁

死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。

golang 中的死锁是当 goroutine 被阻塞而没有任何可能被解除阻塞时发生的状态。

死锁检测器

死锁可能会导致程序异常,因此如果能够在开发阶段就发现死锁可以大大避免程序的崩溃。

golang 提供了一个死锁检测器,可以帮助开发人员检测出代码中写出的死锁代码。

死锁检测器基于对应用程序创建的线程的进行分析,如果创建和活动的线程数高于等待工作的线程数,则会出现死锁情况。

在检测到死锁时,会创建四个线程:

  • 一个用于主 goroutine,启动程序的那个。
  • 一种监视系统的称为 sysmon.
  • 一种专用于垃圾收集器的 goroutines 启动。
  • 在初始化期间主 goroutine 被阻塞时创建的一个线程。

每次线程空闲时,都会通知检测器。调试的每一行都显示增加的空闲线程数。当空闲线程数等于活动线程数减去系统线程数时,就会发生死锁。

但是,这种行为有一些限制。实际上,任何自旋 goroutine 都会使死锁检测器变得无用,因为线程将保持活动状态。

如果你想可视化运行程序上的死锁,可以使用 pprof 之类的工具来可视化它。

产生死锁的原因

goroutine 会产生死锁,要么是因为它正在等待管道消息,要么 是因为它正在等待同步包中的锁。

当没有其他 goroutine 可以访问通道或锁的时候,一组 goroutine 正在等待对方,但没有一个能够继续,这时就会产生死锁。

目前,Go 只检测整个程序何时冻结,而不检测 goroutine 的子集何时产生死锁。

使用管道通常很容易找出导致死锁的原因。但是大量使用互斥锁的程序却很难调试。

go-deadlock

我们知道对于管道的死锁很容易检测,但是对于互斥锁却很难调试发现,因此,有人做出了go-deadlock死锁检测库。

这个死锁检测库并不是基于静态分析的,而是基于运行时检测的。

原理很简单,就是获取当前协程的goroutine id,然后存了当前协程没有释放的lock的对象。 这时候当其他协程去lock的时候,会触发prelock检测,检测有没有冲突lock关系。

相关文章