C# 多线程(三):Thread 和 Task 怎么选
2023 年 7 月 31 日 · 2 分钟读完
目录
前两篇分别讲了 Task 和 Thread 的用法,这一篇比较两者,看各自适合什么场景。
五点差异
| Thread | Task | |
|---|---|---|
| 创建和管理 | 显式创建一个系统线程,手动管理生命周期、优先级、状态 | 属于任务并行库(TPL),把「要做的事」和「在哪个线程做」解耦,默认交给线程池调度 |
| 异步编程 | 创建、启动、等待、结束、同步、通信都要手动处理,代码复杂容易出错 | 配合 async/await,写法接近同步代码,可读性好 |
| 返回值 | ThreadStart 没有返回值,结果只能通过共享变量传出来 |
Task<T> 直接带回结果:int n = await CountAsync(); |
| 异常 | 线程里未处理的异常会直接让整个进程崩溃,外部捕获不到 | 异常保存在 Task 里,await 时在调用方的 try/catch 里抛出 |
| 线程复用 | 每次 new 都分配一个新的系统线程,频繁创建销毁开销大 | 使用线程池,线程反复复用 |
除此之外,Task 还自带取消(CancellationToken)、组合(WhenAll、WhenAny)、延续(ContinueWith)等能力,用 Thread 都要自己实现。
结论
Thread 是传统的多线程方式,什么都要自己管;Task 是现代 C# 推荐的方式,模型更简单、资源利用更好。新代码默认用 Task 和 async/await。
延伸:上位机开发里的选型
| 场景 | 推荐 |
|---|---|
| 点按钮后执行一个耗时操作,做完更新界面 | async void 事件处理 + await Task.Run(...) |
| 调用网络、文件、数据库 | 直接 await 它们的异步 API(HttpClient、ReadAsync) |
| 同时处理多张图像、多个工位 | 多个 Task.Run + Task.WhenAll,或 Parallel.ForEach |
| 软件运行期间一直轮询 PLC 或串口 | 一个后台 Thread,或 Task.Factory.StartNew(..., TaskCreationOptions.LongRunning) |
| 需要设置线程优先级、STA 单元(如调用某些 COM 组件、剪贴板) | Thread |
| 定时执行 | .NET 6 起的 PeriodicTimer:while (await timer.WaitForNextTickAsync(token)) { ... } |
长期运行的循环不建议放进普通的 Task.Run:它会一直占着一个线程池线程,线程池线程不够时,其他任务要排队等新线程,可能出现莫名其妙的延迟。
原文首发于 blog.csdn.net