C# 中的并发与多线程:从 Thread 到 async/await
现代应用程序越来越需要同时执行多个任务:从 Web 服务器处理请求到桌面程序中的并行计算。在 C# (.NET) 中,为此提供了强大的工具集——从经典的线程 (Thread) 到高级别的 async/await 结构和 Task Parallel Library (TPL) 库。在本文中,我们将分析并发和多线程的关键概念、它们的实际应用以及最佳实践。
什么是并发与多线程?
在进入代码之前,理解这些概念之间的区别很重要:
- 并发 (Concurrency) — 程序通过切换任务来执行多个任务的能力,造成同时执行的假象。这也可以在单核处理器上实现。
- 多线程 (Multithreading) — 并发的一种形式,其中多个线程 (threads) 在多核处理器上物理上同时执行。
- 并行 (Parallelism) — 多线程的一个子集,其中任务的不同部分严格同时在不同的内核上执行。
在 C# 中,您既可以使用底层线程,也可以使用自动管理线程的抽象。选择取决于任务:对于 I/O-bound 操作(文件、网络工作),async/await 更合适;而对于 CPU-bound 操作(计算),则使用 Parallel.For 或 Task。
创建和管理线程:Thread 类
System.Threading.Thread 类是 C# 中处理线程的最底层方式。它提供了完全的控制,但需要仔细的同步。
using System;using System.Threading;
class Program{ static void Main() { Thread thread = new Thread(DoWork); thread.Start();
// 主线程继续工作 for (int i = 0; i < 5; i++) { Console.WriteLine($"主线程: {i}"); Thread.Sleep(100); }
thread.Join(); // 等待子线程完成 Console.WriteLine("工作完成。"); }
static void DoWork() { for (int i = 0; i < 5; i++) { Console.WriteLine($"子线程: {i}"); Thread.Sleep(150); } }}手动管理线程的问题:
- 同步复杂(数据竞争、死锁)。
- 创建和销毁线程的开销很高。
- 缺乏对返回结果的内置支持。
因此,在现代 C# 中,更倾向于使用 Task Parallel Library。
Task Parallel Library (TPL) 与 async/await
来自 System.Threading.Tasks 命名空间的 Task 类代表一个异步操作。结合关键字 async 和 await,它允许编写看起来像同步代码的异步代码。
Task 基础示例
using System;using System.Threading.Tasks;
class Program{ static async Task Main() { Console.WriteLine("开始工作。");
// 启动一个异步任务 Task<int> task = CalculateAsync(10);
// 在任务执行期间,我们可以做其他事情 Console.WriteLine("正在做其他工作...");
// 等待结果 int result = await task; Console.WriteLine($"结果: {result}"); }
static async Task<int> CalculateAsync(int number) { await Task.Delay(1000); // 模拟耗时操作 return number * number; }}