用 ThreadPool.QueueUserWorkItem
提交任务最直接
不需要手动创建“线程池对象”,.NET 的
ThreadPool是静态类,全局唯一,开箱即用。你只需要把工作项丢进去,运行时自动调度:
ThreadPool.QueueUserWorkItem(_ =>
{
Console.WriteLine($"Task running on thread: {Thread.CurrentThread.ManagedThreadId}");
});
注意:
QueueUserWorkItem接收一个
WaitCallback委托(即
Action<object></object>),参数是可选的上下文对象。如果不需要传参,用占位符
_即可。
常见错误:试图 new 一个
ThreadPool实例 —— 它没有公共构造函数,
new ThreadPool()编译不通过。
Task.Run
是更现代、推荐的替代方式
虽然底层仍走
ThreadPool,但
Task.Run提供了更好的错误传播、取消支持和 async/await 兼容性:
Task.Run(() =>
{
// 这里执行 CPU 密集型工作
Thread.Sleep(1000);
Console.WriteLine("Done");
});
优势对比:
Task.Run返回
Task,可 await、可 .ContinueWith、可加
cancellationToken
ThreadPool.QueueUserWorkItem不返回句柄,异常会直接终止进程(除非捕获在委托内部) .NET 6+ 中,
Task.Run默认使用
ThreadPool,行为一致,但抽象层级更高
需要自定义线程池?基本没必要,但可调 ThreadPool.SetMinThreads
绝大多数场景下,不要试图“创建新线程池”。.NET 的默认线程池已高度优化。真有特殊需求(比如 IO 密集型服务需更快响应),可微调:
int workerThreads, ioThreads; ThreadPool.GetMinThreads(out workerThreads, out ioThreads); ThreadPool.SetMinThreads(100, ioThreads); // 至少预留 100 个工作线程
但要注意:
SetMinThreads影响的是“最小空闲线程数”,不是最大线程数(上限由系统决定) 设得过高会导致内存占用上升、上下文切换开销变大 ASP.NET Core 等托管环境通常禁止调用此 API,会抛出
PlatformNotSupportedException
别混淆:ThreadPool ≠ TaskScheduler.Default ≠ 自定义 TaskScheduler
容易踩坑的地方:
Task.Run默认用
TaskScheduler.Default,它背后就是
ThreadPool—— 二者不是并列关系,而是包装关系
new Thread(...).Start()是全新线程,不走线程池,开销大,别用来替代 写异步方法时,
await Task.Delay(100)不消耗线程池线程;但
await Task.Run(() => Thread.Sleep(100))才真正占用一个池线程
线程池本质是资源复用机制,不是“你要就给你一个新池子”。理解这点,才能避免过度配置或误用
new Thread。
