.NET 线程池 工作线程 扩容 + 空闲 + 回收 原理
2026/5/10 13:51:42 网站建设 项目流程

0. 先明确 3 个线程池常量

这些是底层硬编码规则,所有逻辑都基于此:

  1. 最小工作线程数(MinThreads)

    • 默认 = CPU 核心数
    • 线程数 ≤ 最小值:立即创建、永不回收
  2. 最大工作线程数(MaxThreads)

    • 默认 = 32767(很大,基本不会触达)
    • 达到上限后,不再创建新线程,任务排队
  3. 两个关键时间

    • 扩容节流时间:超过最小线程后,每 500ms 最多创建 1 个新线程
    • 空闲回收时间:超过最小线程的线程,空闲 1 秒后自动销毁

1. 线程池工作线程:扩容逻辑

理论原理

线程池不会瞬间创建大量线程,而是非常保守地扩容,防止 CPU 爆炸。

扩容流程:

  1. 任务进来 → 检查是否有空闲线程
  2. 无空闲 → 检查当前线程数 < 最小线程数?
    • 是:立即创建新线程(0 等待)
    • 否:进入 500ms 限流创建
  3. 每 500ms 只允许新增1 个线程
  4. 直到达到最大线程数,停止扩容

代码演示:触发线程池扩容

using System; using System.Threading; using System.Threading.Tasks; class ThreadPoolDemo { static void Main() { // 一、手动设置线程池,方便观察:最小 3 个,最大 10 个 ThreadPool.SetMinThreads(3, 3); ThreadPool.SetMaxThreads(10, 10); Console.WriteLine("=== 开始提交 10 个长时间任务 ==="); // 二、一次性扔 10 个任务,触发扩容 for (int i = 0; i < 10; i++) { int num = i; ThreadPool.QueueUserWorkItem(Worker, num); } // 三、后台监控线程池状态(1 秒打印一次) Task.Run(Monitor); Console.ReadLine(); } // 长时间工作任务:让线程一直占用,不释放 static void Worker(object state) { int id = (int)state; Console.WriteLine($"[任务 {id}] 运行中 | 线程ID: {Thread.CurrentThread.ManagedThreadId}"); // 模拟耗时 10 秒,让线程一直忙,迫使线程池不断扩容 Thread.Sleep(10000); } // 监控:打印繁忙线程数、空闲线程数 static async Task Monitor() { while (true) { ThreadPool.GetMaxThreads(out int maxWorker, out _); ThreadPool.GetAvailableThreads(out int availWorker, out _); int busyWorker = maxWorker - availWorker; Console.WriteLine($"\n[监控] 繁忙工作线程:{busyWorker} | 可用空闲:{availWorker}"); await Task.Delay(1000); } } }

运行结果分析(扩容现象)

你会看到控制台输出:

  1. 3 个任务立即执行(因为最小线程 = 3)
  2. 第 4 个任务不会马上运行,要等 500ms才会新建线程运行
  3. 第 5、6… 个任务每隔 500ms 才会运行一个
  4. 线程数缓慢上涨,直到 10 个(最大线程)

这就是500ms 扩容限流

2. 线程池工作线程:空闲逻辑

当线程执行完任务,不会立刻销毁,而是进入空闲等待状态

  1. 线程完成任务
  2. 去线程池队列尝试获取下一个任务
  3. 没有任务 → 进入空闲等待
  4. 空闲时:
    • 不占用 CPU
    • 处于内核等待状态
    • 随时可以被新任务唤醒
    • 线程依然存活,只是休眠

观察空闲状态

static void Main() { ThreadPool.SetMinThreads(2, 2); Console.WriteLine("提交 1 个短任务"); ThreadPool.QueueUserWorkItem((_) => { Console.WriteLine("任务执行完成"); // 执行完后,线程不会退出,进入空闲等待 }); Console.ReadLine(); }

现象

任务执行完后,线程不会消失,它回到线程池,处于空闲休眠状态。

3. 线程池工作线程:回收逻辑

线程池只回收多余的线程,最小线程数以内的永久保活。

回收规则:

  1. 线程处于空闲等待
  2. 空闲时间 ≥1 秒
  3. 当前总线程数> 最小线程数满足:线程被销毁、回收、释放内存
  4. 当前总线程数≤ 最小线程数不回收,永久保活
    最小以内永久留,超出部分闲 1 秒就杀。

    线程回收

    static void Main() { // 最小 2 个线程 ThreadPool.SetMinThreads(2, 2); ThreadPool.SetMaxThreads(10, 2); Console.WriteLine("=== 提交 5 个任务,触发扩容 ==="); for (int i = 0; i < 5; i++) { ThreadPool.QueueUserWorkItem((_) => { Console.WriteLine($"任务运行 | 线程ID:{Thread.CurrentThread.ManagedThreadId}"); Thread.Sleep(2000); // 运行 2 秒 Console.WriteLine("任务结束"); }); } Task.Run(Monitor); Console.ReadLine(); }

    运行结果分析(回收现象)

  5. 运行时,繁忙线程数涨到5
  6. 2 秒后所有任务结束
  7. 等待1 秒
  8. 你会看到监控:繁忙线程从 5 → 慢慢降到 2(超过最小线程的 3 个线程被回收销毁)

4. 流程图

任务来了

新任务 → 有空闲线程?→ 是:直接执行 ↓ 否 当前线程数 < 最小线程数?→ 是:立即新建线程 ↓ 否 进入 500ms 等待 → 创建 1 个线程 重复直到达到最大线程数

任务执行完

任务结束 → 线程回到线程池 → 等待新任务 ↓ 等待超时(1秒) 当前线程数 > 最小线程数?→ 是:销毁线程(回收) ↓ 否 继续等待(永久保活)

5. 总结

  • 扩容小于最小线程:立即创建大于最小线程:每 500ms 创建 1 个

  • 空闲线程执行完任务,不退出,休眠等待新任务,不占 CPU

  • 回收大于最小线程的部分:空闲 1 秒 → 销毁小于等于最小线程:永久不回收

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询