线程池 SetMaxThreads 和 SetMinThreads 的魔数问题

38

设置ThreadPool的SetMaxThreads和SetMinThreads的值是否有一个神奇的数字或公式?我有成千上万个需要执行的长时间运行的方法,但找不到这些值的完美匹配。任何建议将不胜感激。

3个回答

54

默认的最小线程数为您的机器核心数量。这是一个不错的数字,通常没有必要运行超过核心数量的线程。

默认的最大线程数在.NET 2.0 SP1及以上版本中为核心数量的250倍。这里有很大的空间。在四核机器上,如果没有任何线程在合理的时间内完成,则需要499秒才能达到该最大值。

线程池调度程序尝试将活动线程数限制为最小值,默认情况下为您的核心数量。每秒钟允许一个额外的线程启动,如果活动线程没有完成。运行时间很长或执行了很多不是由I/O引起的阻塞的线程不适合使用线程池。应该使用普通的线程。

达到最大值并不健康。在四核机器上,仅这些线程的堆栈就会消耗1GB的虚拟内存空间。非常有可能发生OOM。如果这是您的问题,请考虑降低最大线程数。或者考虑仅启动一些普通线程,从线程安全队列接收工作包。


请看一下我的相关问题,有位用户建议我使用[线程池],但根据您所说,我认为这不是个好主意,也许您可以帮我解决问题。 - Shimmy Weitzhandler
4
@Hans Passant:这个说法基本正确,首先,默认的最大线程数取决于应用程序使用的.NET Framework版本(例如,在2.0中,每个核心的默认最大线程数为25)。其次,增加最小线程数会导致线程能够立即启动,直到达到最小数量(如果不需要,则不会启动)。如果达到最小延迟,新线程将只在之间有延迟时创建,从而在长时间操作(>500ms)上提高性能,但在短时间操作上可能会降低性能。 - haze4real
1
@HansPassant - 如果创建的线程可能会因等待外部IO而被阻塞,我认为在某些情况下,“最佳”最小值确实应该高于核心数。因为“阻塞”的线程仍然“计入”最小值,对吧?(我明白这对于几秒钟以上的时间不重要,因为此后调度程序将创建这些额外的线程。只是想确保我理解了推理过程。) - ToolmakerSteve

9
通常情况下,不需要调整线程池的线程数量。线程池会很好地处理这个问题。
但是,如果你有很多长时间运行的服务,而且这些服务会有大量等待时间,你可能需要增加最大线程数以处理更多选项。(如果进程没有阻塞,增加线程数可能会减慢速度...)
请对应用程序进行分析以找到正确的数量。

如果我要更改最大线程数(和最小线程数?),有两个参数,工作线程和完成端口线程。完成端口线程计数是用来做什么的? - Benny
此外,如果我让线程池(ThreadPool)保持原状,不设置最小/最大线程数,则会遇到OutOfMemoryException异常,所有线程都将失败。 - Benny
3
那是一个不同的问题 - 你可以通过限制速率来减少工作量,但内存耗尽与线程无关... - Reed Copsey
@ReedCopsey,你能否帮我解决这个相关问题?谢谢。 - Shimmy Weitzhandler
不建议通过“节流”工作来避免由于线程池使用过多线程而导致的OOM异常。线程池的理念是管理异步工作负载,因此您应该将“节流”留给线程池而不是自己去做。如果由于线程池而耗尽内存,则应减少MaxThreads(1个线程大约需要1 MB)。 - haze4real
@haze4real - 可能OOM并不是由于ThreadPool直接发生的,而是因为程序员创建了每个使用大量内存的线程。在这种情况下,“节流”正是程序员需要做的:限制特定任务的活动线程数。然而,我同意调整线程池参数并不是一种很好的节流方式 - 相反,应该在源头上进行“节流”:启动这些线程的代码应该跟踪活动线程的数量,并在达到自我设定的限制时阻止自己。 - ToolmakerSteve

7

我尝试使用SmartThreadPool,但没有成功。在我排队所有工作项之后,我尝试使用WaitForIdle(我有后执行逻辑要运行),但它从未阻塞,直接继续执行。可能是键盘和计算机之间的问题,但我还没有找出来。 - Benny
1
我知道这篇文章已经发表了10年。但是就我个人的经验而言,在几乎所有情况下,认为“自定义”实现比.NET团队多年来制作的更好是一个糟糕的决定。当然,可能存在极端情况,通用线程池实现可能不太理想,但这种情况非常罕见。 - Hulvej

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