线程池行为:不从最小大小增长

3

我已经按照以下方式设置了我的线程池:

ThreadPool.SetMaxThreads(10000, 10000);
ThreadPool.SetMinThreads(20, 20);

然而,在重负载下我的应用开始出现挂起现象。这似乎是因为工作任务没有执行:我使用了ThreadPool.QueueUserWorkItem来运行一些任务,这些任务又使用相同的方法来排队其他工作。显然,这在有限线程池中是很危险的(死锁情况),但我使用线程池不是为了限制最大线程数,而是为了减少线程创建开销。
我看到那里的潜在陷阱,但我相信设置池中最多10000个线程将意味着如果一个项被排队,所有线程都忙碌,并且池中没有10000个线程,那么一个新线程将被创建并在那里处理任务。
然而,我改成了这样:
ThreadPool.SetMaxThreads(10000, 10000);
ThreadPool.SetMinThreads(200, 200);

...应用程序开始运行。如果这使它开始工作,那么我是否忽略了线程池从最小值向最大值扩展的方式/时间的某些内容?

4个回答

4
无论何时使用线程池,你都要受其“线程注入和退役算法”的支配。
该算法并没有得到适当的文档记录(据我所知),也不可配置。
如果你正在使用任务,你可以编写自己的任务调度程序

4
线程池调度程序的工作是确保没有比CPU核心更多的正在执行的TP线程。默认最小值等于核心数,这是一个幸福的数字,因为它最大程度地减少了线程上下文切换的开销。每秒钟两次,如果现有线程尚未完成,调度程序就会介入并允许另一个线程执行。
因此,要达到新的最大值需要一小时二十分钟的线程不完成。这是相当不可能的,在32位机器上,当2000个线程消耗所有可用虚拟内存时,会出现问题。在64位操作系统上,通过使用非常大的分页文件,您可以尝试达到这个最大值。需要大量的RAM来避免分页死亡,至少需要12GB。
通常的诊断是您不适当地使用了TP线程。它们花费太长时间,通常是由于I/O阻塞引起的。对于这种类型的工作,常规线程才是正确的选择。现在修复可能很困难,特别是因为您对目前的情况感到满意。提高最小值确实是一种快速解决方法。由于TP调度程序无法再做出合理的工作,您将不得不手动进行调整。

谢谢,非常有启发性。我目前正在考虑一台具有8个核心、64位的机器,内存为68GB。我相信我确实需要在短时间内执行大量任务。正如尼古拉斯建议的那样,我认为TPL/任务计划程序可能是正确的选择。 - Kieren Johnstone

2
您所描述的性能问题,与此ASP.NET KB文章中记录的类似。

http://support.microsoft.com/kb/821268

总结一下,您需要仔细选择参数(本文提到了默认ASP.NET线程池的典型设置,但您可以将此技巧应用于您的应用程序),并根据性能测试和您的应用程序特性进一步调整它们。
请注意,您了解负载的越多,就会发现“重负载”不再是描述情况的好词语。有时候您需要进一步分类这些情况,包括详细术语,例如突发负载等。

谢谢。我有一连串的主要任务,这些任务会在短时间内产生相对较多的并发次要任务。不确定术语是什么 :) 但通常我认为我的问题将通过拥有一个主任务线程池和一个次要任务线程池来解决,每个主任务都有一个初始的规划/分配阶段(在该点同步以避免死锁)。 - Kieren Johnstone

1

如果您的逻辑依赖于最少线程数,那么您需要立即更改。

设置200(甚至20)的MinThreads会浪费相当多的内存。请注意,MaxThreads在这里不相关,您可能没有10 GB的内存可用。

最小200个线程对您有所帮助这一事实是可疑的,并且作为解决方案,它可能非常脆弱。

查看正常的生产者/消费者模式,和/或使用有界队列来耦合您的任务。


谢谢,但你知道我的问题的答案吗?我对我的应用程序设计感到满意;我可以轻松地连接20个客户端,每个客户端可同时处理10-20个并发任务,在短时间内使用。 - Kieren Johnstone

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接