线程池的优化(SetMaxThreads和SetMinThreads)

当启动一个线程时,会有几百毫秒的时间花费在准备一些额外的资源上,例如一个新的私有局部变量栈这样的事情。每个线程会占用(默认情况下)1MB 内存。线程池(thread pool)可以通过共享与回收线程来减轻这些开销,允许多线程应用在很小的粒度上而没有性能损失。在多核心处理器以分治(divide-and-conquer)的风格执行计算密集代码时将会十分有用。

线程池会限制其同时运行的工作线程总数。太多的活动线程会加重操作系统的管理负担,也会降低 CPU 缓存的效果。一旦达到数量限制,任务就会进行排队,等待一个任务完成后才会启动另一个。这使得程序任意并发成为可能,例如 web 服务器。(异步方法模式(asynchronous method pattern)是进一步高效利用线程池的高级技术)。

优化线程池是基于什么原理?

线程池初始时其池内只有一个线程。随着任务的分配,线程池管理器就会向池内“注入”新线程来满足工作负荷的需要,直到最大数量的限制。在足够的非活动时间之后,线程池管理器在认为“回收”一些线程能够带来更好的吞吐量时将进行线程回收。

可以通过调用ThreadPool.SetMaxThreads方法来设置线程池可以创建的线程上限;默认如下:

  • Framework 4.0,32位环境下:1023
  • Framework 4.0,64位环境下:32768
  • Framework 3.5:每个核心 250
  • Framework 2.0:每个核心 25

(这些数字可能根据硬件和操作系统不同而有差异。)数量这么多是因为要确定阻塞线程的条件(等待一些条件,比如远程计算机的相应)是否被满足。

也可以通过ThreadPool.SetMinThreads设置线程数量下限。下限的作用比较奇妙:它是一种高级的优化技术,用来指示线程池管理器在达到下限之前不要延迟线程的分配。当存在阻塞线程时,提高下限可以改善程序并发性能。

默认下限数量是 CPU 核心数,也就是能充分利用 CPU 的最小数值。在服务器环境下(比如 IIS 中的 ASP.NET),下限数量一般要高的多,差不多 50 或者更高。

最小线程数量是如何起作用的?

将线程池的最小线程数设置为 x 并不是立即创建至少 x 个线程,而是线程会根据需要来创建。这个数值是指示线程池管理器当需要的时候,立即创建 x 个线程。那么问题是为什么线程池在其它情况下会延迟创建线程?

答案是为了防止短生命周期的任务导致线程数量短暂高峰,使程序的内存占用(memory footprint)快速膨胀。为了描述这个问题,考虑在一个 4 核的计算机上运行一个客户端程序,它一次发起了 40 个任务请求。如果每个任务都是一个 10ms 的计算,假设它们平均分配在 4 个核心上,总共的开销就是 100ms 多。理想情况下,我们希望这 40 个任务运行在 4 个线程上:

  • 如果线程数量更少,就无法充分利用 4 个核心
  • 如果线程数量更多,会浪费内存和 CPU 时间去创建不必要的线程。

线程池就是以这种方式工作的。让线程数量和 CPU 核心数量匹配,就能够既保持小的内存足迹,又不损失性能。当然这也需要线程都能被充分使用(在这个例子中满足该条件)。

但是,现在来假设任务不是进行 10ms 的计算,而是请求互联网,使用半秒钟(500ms)等待响应,此时本地 CPU 是空闲状态。线程池管理器的线程经济策略(译者注:指上面说的线程数量匹配核心数)这时就不灵了,应该创建更多的线程,让所有的请求同时进行。

幸运的是,线程池管理器还有一个后备方案。如果在半秒内没有能够响应请求队列,就会再创建一个新的线程,以此类推,直到线程数量上限。

半秒的等待时间是一把双刃剑。一方面它意味着一次性的短暂任务不会使程序快速消耗不必要的40MB(或者更多)的 Memory。另一方面,在线程池线程被阻塞时,比如在请求数据库或者调用WebClient.DownloadFile,CPU 就进行不必要的等待。因为这种原因,你可以通过调用SetMinThreads来让线程池管理器在分配最初的 x 个线程时不要等待,例如:

ThreadPool.SetMinThreads (50, 50);

(第二个参数是表示多少个线程分配给 I/O 完成端口(I/O completion ports,IOCP),来被APM使用。)

最小线程数量的默认值是 CPU 核心数。

原文链接:C# 中的多线程 - GKarch

===================================

一个形象的类比:你是某饭店著名大厨(CPU内核),你的工资老高了(实话实说老板很心疼你的高工资),饭店(营业面积有限—内存)有大批客户在排队等你做出饭菜,有一部分客户要吃燕窝鱼翅......等等多种菜品,但是你的食材有限,意味着客户需要等待饭店的采购去各个市场购买食材回来;有一部分只吃简单的面食,你有现成的食材,无需等待。这个饭店的老板想让高效利用(压榨)你给他挣钱,就会想办法招揽两种类型的客户进店消费,而两种类型的客户各自招揽多少,就是老板要精心计算的了(线程池管理器要起作用了)。

posted on 2026-03-23 07:33  喝咖啡就蒜瓣儿  阅读(0)  评论(0)    收藏  举报

导航