| 版本 | 关键特性 | 示例 |
|---|---|---|
| C# 4.0 | Task 类型引入 | Task.Run(() => {}) |
| C# 5.0 | async/await | await SomeAsyncMethod() |
| C# 6.0 | 在catch/finally中使用await | try {} catch {} |
| C# 7.0 | ValueTask | async ValueTask<int> |
| C# 8.0 | 异步流 await foreach | await foreach (var item in stream) |
| C# 9.0 | 模块初始化器 | [ModuleInitializer] |
| C# 10.0 | 异步方法构建器改进 | 性能优化 |
你: "我要一个汉堡" 服务员: "好的,请稍等" ↓ 服务员走到厨房,等着厨师做汉堡 (5分钟什么都不做,就在厨房等着) ↓ 服务员拿着汉堡回来:"您的汉堡" 你继续等服务员
你: "我要一个汉堡" 服务员: "好的,我去下单" ↓ 服务员给厨房下单,然后离开 ↓ 服务员去服务其他顾客 ←── 可以并行工作 ↓ 厨房:"汉堡好了!" 服务员回来取汉堡给你
csharp// 同步等待 - 线程被完全占用
public void ProcessOrder()
{
var task = MakeHamburger(); // 开始制作汉堡
var hamburger = task.Result; // 阻塞:线程在这里干等,什么都不能做
ServeToCustomer(hamburger);
}
// 异步等待 - 线程可以被释放
public async Task ProcessOrderAsync()
{
var task = MakeHamburgerAsync(); // 开始制作汉堡
var hamburger = await task; // 非阻塞:线程可以去做别的事
// 注意:await返回后,可能不是原来的线程在执行!
ServeToCustomer(hamburger);
}
时间线: |=======线程A=======| 任务: 下单 → 等待汉堡 → 上菜 ↓ ↓ 线程一直占用,无法处理其他请求
时间线: |==线程A===| |===线程X===| 任务: 下单 → 释放 → 处理其他请求 → 返回上菜 ↑ 汉堡在后台制作 可能由其他线程完成
csharp// 同步方法 - 线程在"等待"
public IActionResult SyncMethod()
{
Thread.CurrentThread.ManagedThreadId; // 线程ID: 1
// 线程在这里被阻塞,什么都不能做
Thread.Sleep(5000); // 模拟IO等待5秒
Thread.CurrentThread.ManagedThreadId; // 线程ID: 1 (同一线程)
return Ok();
}
// 异步方法 - 线程可以"去忙别的"
public async Task<IActionResult> AsyncMethod()
{
Thread.CurrentThread.ManagedThreadId; // 线程ID: 1
// 关键:await不会阻塞线程
await Task.Delay(5000); // 异步等待5秒
// 在此期间,线程1可以去处理其他HTTP请求!
Thread.CurrentThread.ManagedThreadId; // 线程ID: 可能是3或4 (不同线程)
return Ok();
}
| 特性 | Thread.Sleep(5000) | await Task.Delay(5000) |
|---|---|---|
| 线程状态 | 阻塞(Blocked) | 释放(Released) |
| 可处理其他请求 | ❌ 不能 | ✅ 能 |
| 线程数需求 | 每个请求一个线程 | 少数线程服务多个请求 |
| 适合场景 | 极少使用 | Web服务器、IO操作 |
假设有100个并发请求:
线程池:有10个线程 100个请求 → 10个线程处理 → 90个请求排队等待 每个线程处理一个请求时,其他请求必须等待
线程池:有10个线程 100个请求 → 10个线程轮流服务 线程在await时释放 → 可以服务其他请求 所有请求几乎同时处理
await确实在等待,但线程不等待
csharp// 你的代码在等待,但线程不等待
await SomeTask(); // 这里:你的方法暂停,但线程被释放
不是"多线程"
csharp// 错误理解:开了一个新线程
// 正确理解:当前线程被释放,任务完成后由某个线程继续
异步 ≠ 并行
csharp// 模拟100个并发请求
public async Task Test()
{
// 同步:可能需要100个线程
// 异步:可能只需要10-20个线程
// 内存使用:
// 同步:每个线程约1MB栈内存 → 100MB
// 异步:少量状态机对象 → 约几KB
}
问自己:"在等待的时候,这个线程能不能去做别的工作?"
await(异步)希望这个解释让你明白了await和同步等待的本质区别!
本文作者:张京
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!