天天看点

NET中如何优雅地调优ThreadPool

作者:中年农码工

读完这篇文章里你能收获到

了解线程池不足的常见现象

解析引起线程池不足的原因

根据不同的需求场景提出不同的优化措施

感谢点赞+收藏,避免下次找不到~

文章目录

一、ThreadPool 资源不足会有引起哪些现象

二、导致 ThreadPool 资源不足的常见原因

三、调优 ThreadPool 的措施

1. 使用异步编程模型

2. 使用 Task.Factory.StartNew

3. 长时间运行的任务处理

4. 合理设置线程池参数

四、.NET中如何合理设置线程池参数

1. 线程池最大线程数

2. 线程池最小线程数

3. 线程池队列长度

4. 线程池工作线程优先级

一、ThreadPool 资源不足会有引起哪些现象

任务执行时间变长:由于Task.Run 和 ThreadPool.QueueUserWorkItem 都是将任务提交给线程池执行,当线程池中的工作线程不足时,需要等待其他任务执行完成后才能得到执行,因此会导致整体的任务执行时间会变长。

程序崩溃:如果线程池中没有可用的工作线程,新的任务将无法得到执行,从而导致程序崩溃或出现异常。

内存泄漏:如果使用了大量的异步任务,而线程池中的工作线程数不足,会导致这些任务一直在等待线程池资源,从而占用大量的内存资源,引发内存泄漏。

服务器性能下降:如果是 Web 应用程序,当线程池资源不足时,可能会导致服务器的性能下降,响应时间变长,甚至无法响应客户端请求。

二、导致 ThreadPool 资源不足的常见原因

阻塞操作:如果您的代码中存在阻塞操作(例如 I/O 操作),并且这些操作使用了同步方式,这可能会导致线程池中的线程被阻塞。当线程被阻塞时,它不能被用于执行其他任务,从而导致线程池资源不足。

长时间运行的任务:如果您的代码中存在长时间运行的任务(例如计算密集型操作),这可能会导致线程池中的线程被占用。当线程被占用时,它不能被用于执行其他任务,从而导致线程池资源不足。

不恰当的线程使用:如果您的代码中创建了大量的线程(例如使用 Thread 类),而不是使用线程池,这可能会导致线程池资源不足。因为线程池的目的是重复使用线程来执行任务,如果您自己创建线程,则会降低线程池的效率。

线程泄漏:如果您的代码中存在线程泄漏(例如线程未正确释放),这可能会导致线程池资源不足。因为线程池中的线程是有限的,如果您的代码中存在泄漏的线程,则这些线程将一直占用线程池资源,直到应用程序退出。

三、调优 ThreadPool 的措施

1. 使用异步编程模型

使用 async/await:使用异步编程模型,可以避免线程被阻塞,从而释放线程池资源。

//【避免】会阻止线程,这是导致 ThreadPool 资源不足的最常见原因

Customer c = PretendQueryCustomerFromDbAsync("Dana").Result;

//【建议】使用 await 可让当前线程在数据库查询过程中为其他工作项提供服务

Customer c = await PretendQueryCustomerFromDbAsync("Dana");

2. 使用 Task.Factory.StartNew

使用 Task.Factory.StartNew 方法:可以使用 Task.Factory.StartNew 方法,手动创建一个新的任务,而不是使用线程池中的工作者线程执行任务,以避免线程池资源不足导致任务无法执行的情况

Task.Factory.StartNew(() => {

// 执行任务

});

3. 长时间运行的任务处理

尽可能避免长时间运行的任务:尽可能使用短时间运行的任务,避免使用计算密集型操作,如需长时间运行任务(比如与应用生命周期相同),可以通过在调用 Task.Factory.StartNew 方法时传入 TaskCreationOptions.LongRunning 参数,可以通知 TaskScheduler 创建一个新线程来执行该任务。这种方式可以避免使用线程池线程来执行长时间运行的任务,从而避免线程池资源不足的情况。

Task.Factory.StartNew(() => {

// 执行长时间运行的任务

}, TaskCreationOptions.LongRunning);

4. 合理设置线程池参数

参考以下

四、.NET中如何合理设置线程池参数

1. 线程池最大线程数

应根据应用程序的负载情况来设置,以确保最大限度地利用计算资源。在设置最大线程数时,应该考虑到应用程序中存在的所有线程和其他系统资源的使用情况。

//将线程池的最大工作者线程数设置为当前最大线程数的两倍

int workerThreads, completionPortThreads;

ThreadPool.GetMaxThreads(out workerThreads, out completionPortThreads);

ThreadPool.SetMaxThreads(workerThreads * 2, completionPortThreads);

2. 线程池最小线程数

应根据应用程序的负载情况来设置,以确保始终有足够的线程可用于执行任务。一般情况下,线程池最小线程数应该设置为 0,以允许线程池动态调整线程数。

//将线程池的最小工作者线程数和 I/O 完成端口线程数都设置为 10。

ThreadPool.SetMinThreads(10, 10);

3. 线程池队列长度

应根据应用程序的负载情况来设置。队列长度应该设置得足够大,以容纳瞬时的任务突发,并允许线程池动态调整队列长度。

//将线程池的队列长度设置为 1000

ThreadPool.SetMaxQueuedWorkItems(1000);

4. 线程池工作线程优先级

应根据应用程序的性能要求和响应时间要求来设置。如果应用程序需要高性能,可以将线程池工作者线程的优先级设置为 ThreadPriority.AboveNormal 或 ThreadPriority.Highest。

//将当前线程(工作者线程)的优先级设置为 ThreadPriority.Highest。

Thread.CurrentThread.Priority = ThreadPriority.Highest;

NET中如何优雅地调优ThreadPool

继续阅读