如果您是使用.net或.net core平台编写服务时服务负载能力已经达到顶峰,而CPU并没有得到充分利用,看看这篇文章,那么或许你可以得到一些启发。
目录
1、问题现象
2、线程数不够
3、什么是线程池
4、什么是异步编程?
5、通常是什么导致阻塞?
6、我怎么知道ThreadPool缺少线程?
6.1、PerfView / threadTime收集
6.2、寻找增长的线程数。
6.3、查找阻塞API
6.4、主动出击
6.5、 解决方法:在ThreadPool中强制使用更多线程
7、小结
1、问题现象
有一个api网关,采用的是.net core 2.1开发,其并发量达到一定程度时再难以上升,此时分析瓶颈存在哪里,在之前已经有所猜测,此处也算是证实下猜想。
在这里插入图片描述
本接口仅仅访问了redis、速度应该很快,然而并发达到300左右,已经卡成翔了。
2、线程数不够
有一种情况,那就是除了其他的I/O慢外,就是线程资源不够。当执行并发操作时,没有线程可以运行下一步来处理该请求,因此它只是停顿,等待此类线程可用。这段时间显示为“更长的”IO查询或处理,但是它发生时其实没有任何非CPU活动发生,因此,此类问题被称为ThreadPool饥饿,这是本文的重点。
3、什么是线程池
在讨论线程池之前,我们应该返回并描述什么是线程。
线程是执行顺序程序的最小单位,包括执行方法的“调用堆栈”,以及每个方法的所有局部变量。核心点是所有代码都需要线程才能“运行”。程序启动时,将被分配一个线程,多线程程序会创建其他线程,每个线程彼此同时执行代码。
在并发程度适中的世界中,线程是有意义的,每个线程都在执行复杂的操作。
但是,某些工作负载具有完全相反的特征:发生了许多并发事件,并且每个工作都在做简单的事情。由于所有执行都需要一个线程,因此对于这种工作负载,重用该线程很有意义,因为它可以执行许多小的(不相关的)工作项。这就是线程池的来历,它有一个非常简单的API。在.NET中,ThreadPool.QueueUserWorkItem需要委托(方法)来运行。当您调用QueueUserWorkItem时,线程池将承诺运行将来传递的委托。(请注意,.NET不鼓励直接使用线程池。
4、什么是异步编程?
过去,服务使用“多线程”执行模型,该服务将为每个要处理的并发请求创建一个线程。每个这样的线程将从头到尾完成该特定请求的所