一、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;