上一篇博文给你们分享了使用Windbg分析内存泄露问题:html
本篇咱们继续跟你们分享,如何分析解决线程阻塞问题。优化
从根本上讲,线程阻塞属于程序Hang的一种,其表现主要有:spa
1. 随着请求的增长,线程数一直增长,可能会把线程池打爆操作系统
2. 低CPU使用率(被阻塞后的CPU使用率下降)线程
3. 请求没有返回,客户端一直在等待,直至Timeout。3d
那么,从线程状态上看,什么是阻塞? 一个线程经历的5个状态,建立,就绪,运行,阻塞,终止。各个状态的转换条件以下图:调试
上图中有个阻塞状态,就是说当线程中调用某个函数,须要IO请求,或者暂时得不到竞争资源的,操做系统会把该线程阻塞起来,避免浪费CPU资源,等到获得了资源,再变成就绪状态,等待CPU调度运行。code
线程发生阻塞的现象就是,进程的线程数会愈来愈多!htm
线程阻塞问题的分析思路:
持续请求过程当中,抓两个或者三个Dump,看线程增长的速度,每次抓Dump间隔30s或者1分钟
对比的看每一个Dump中:
1. 查看线程池的大小:!threadpool,有时间间隔两个Dump对比着看,看线程池的线程数的增加状况:
2. 查看线程的分类和线程的状态 !threads,从下图能够看出,后台线程一直在增长
3. 查看线程阻塞 !syncblk,也是看这两个dump,对比着看
咱们发现:
第一个Dump中95号线程 阻塞了(1021-1)/2=510个线程
第二个Dump中79号线程 阻塞了(1099-1)/2=549个线程
95号线程独占的对象资源 00000026ba7c4dc0 (System.Object)
79号线程独占的对象资源也是00000026ba7c4dc0(System.Object)
两个线程同时锁住了同一个资源!00000026ba7c4dc0(System.Object)
此时,线程阻塞问题已经肯定,接下来,咱们要重点分析阻塞的根源线程(持有什么资源不释放,致使其余线程在等待),95号线程、79号线程
4. 查看阻塞线程的根源线程、线程请求的资源对象 、被阻塞的线程数
例如 95号线程:
1 查看阻塞根源线程的堆栈 2 ~95s 3 !clrstack
经过线程堆栈,咱们在栈顶发现,95号线程,在等待Socket Server返回,具体再等哪一个Socket Server?
经过如下命令找出当前线程堆栈上的Socket对象
!dso
这样咱们就定位出95号线程在作什么,在等待什么:
95号线程在等待SocketServer 10.*.*.*:80返回数据,独占资源:00000026ba7c4dc0(System.Object)
同时咱们经过线程堆栈看到了咱们本身的一个TCP通信类TCPInvoker, 在建立一个新的TCP链接,TCPInvoker类的代码须要重点关注,继续看!
咱们继续看79号线程:
1 ~79s 2 !clrstack
经过79号线程的堆栈和阻塞状况能够发现:
79号线程Monitor.Enter(object),在请求资源的独占锁:00000026ba7c4dc0(System.Object)
TCPInvoker卡在了GetInvoker方法上。咱们须要看看TCPInvoker的代码了
5. 分析线程阻塞的缘由,改进代码
从以下的代码中,咱们能看到:
95号线程是执行到了GetInvoker方法的Create,Create中在等待Socket Server返回,此时若是Socket Server没有响应,超时时间默认是5s,会一直持有资源00000026ba7c4dc0(System.Object)不释放
79号线程也执行到了GetInvoker方法,可是在Lock时,等待95号线程释放资源:00000026ba7c4dc0(System.Object)
随着请求愈来愈多,超时+重试链接Socket Server,致使线程阻塞住了。
解决方案:1. 分析Socket Server为何连不上 2. 优化改进TCPInvoker内部的业务逻辑,减小超时和重试时间,减小阻塞的产生概率。
好了,上面就是此次分享的Windbg调试线程阻塞问题的细节和过程,总结一下:
线程阻塞问题的分析思路:
周国庆
2018/11/1