正在开会,突然线上站点线程数破千。然后一群人现场dump分析。

先看一眼线程运行状态 !eeversion

记一次Windb死锁排查-LMLPHP

发现CPU占用并不高,19%,937条线程正在运行。

看看他们都在干什么。 ~* e !clrstack

记一次Windb死锁排查-LMLPHP

发现大片内容相似的,并且最后一行是System.Threading.Monitor.Enter,尝试获取锁。很大概率是死锁了,排查一下是否存在死锁的情况。

运行 !syncblk 查看当前的锁的情况

记一次Windb死锁排查-LMLPHP

等待数并不是真的等待数,需要(线程数  -1) / 2,至于具体为什么这么算我就不清楚了。将所有的数据相加 正好是等于937。也就是说所有的线程都在运行,所有的线程都得等待锁,所以肯定出现死锁了。复制内容出来备用。

从第一个线程 90 开始查。~90s,进入90号线程上下文,然后打印堆栈信息 !clrstack -l

记一次Windb死锁排查-LMLPHP

上下文信息中只有这个有值,这个很大概率就是锁对象的地址。然后去锁对象列表中查一下

记一次Windb死锁排查-LMLPHP

果然是锁对象,也就是说90号线程应该是在等77号线程。那么77号线程在等什么?切到77号线程,然后打印上下文。

记一次Windb死锁排查-LMLPHP

发现也是类似的情况,最后在申请锁。我们再查一下这个锁是什么情况。

记一次Windb死锁排查-LMLPHP

77号在等70号线程。那么70号线程在等谁?切换上下文到70号线程,然后打印上下文。

记一次Windb死锁排查-LMLPHP

发现他也在等一个锁对象,我们查一下这个锁对象的拥有者是咋回事。

记一次Windb死锁排查-LMLPHP

我们发现 70线程在等77号。那么现在70号跟77号在相互等待,那么这两个也就死锁了,其他的相关线程大概率都是跟这个死锁相关的。既然是这样,我们分别打印一下77号和70号相关的调用堆栈,就可以对比着代码查一下,为什么会出现死锁了。

记一次Windb死锁排查-LMLPHP

从这个函数名字上看很大概率是IncrementConnection和CloseOnIdle函数发生了死锁的情况,上下文其实也算是相关的。剩下的就只能对比代码,为什么这两个函数可能发生死锁了。

记一次Windb死锁排查-LMLPHP

05-26 05:15