2026-01-07
C#
00

目录

♥C#各版本对异步的关键改进
🍔 快餐店比喻
情况1:同步等待(阻塞)
情况2:异步等待(await)
🔧 技术层面解释
📊 线程时间线对比
同步(线程A一直忙碌):
异步(线程A可以去干别的事):
💻 代码实例对比
🎯 核心区别总结
🌐 Web服务器中的实际效果
同步服务器
异步服务器
⚠️ 重要澄清
📈 性能对比数据
✅ 简单判断方法

♥C#各版本对异步的关键改进

版本关键特性示例
C# 4.0Task 类型引入Task.Run(() => {})
C# 5.0async/awaitawait SomeAsyncMethod()
C# 6.0在catch/finally中使用awaittry {} catch {}
C# 7.0ValueTaskasync ValueTask<int>
C# 8.0异步流 await foreachawait foreach (var item in stream)
C# 9.0模块初始化器[ModuleInitializer]
C# 10.0异步方法构建器改进性能优化

🍔 快餐店比喻

情况1:同步等待(阻塞)

你: "我要一个汉堡" 服务员: "好的,请稍等" ↓ 服务员走到厨房,等着厨师做汉堡 (5分钟什么都不做,就在厨房等着) ↓ 服务员拿着汉堡回来:"您的汉堡" 你继续等服务员

情况2:异步等待(await)

你: "我要一个汉堡" 服务员: "好的,我去下单" ↓ 服务员给厨房下单,然后离开 ↓ 服务员去服务其他顾客 ←── 可以并行工作 ↓ 厨房:"汉堡好了!" 服务员回来取汉堡给你

🔧 技术层面解释

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=======| 任务: 下单 → 等待汉堡 → 上菜 ↓ ↓ 线程一直占用,无法处理其他请求

异步(线程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操作

🌐 Web服务器中的实际效果

假设有100个并发请求:

同步服务器

线程池:有10个线程 100个请求 → 10个线程处理 → 90个请求排队等待 每个线程处理一个请求时,其他请求必须等待

异步服务器

线程池:有10个线程 100个请求 → 10个线程轮流服务 线程在await时释放 → 可以服务其他请求 所有请求几乎同时处理

⚠️ 重要澄清

  1. await确实在等待,但线程不等待

    csharp
    // 你的代码在等待,但线程不等待 await SomeTask(); // 这里:你的方法暂停,但线程被释放
  2. 不是"多线程"

    csharp
    // 错误理解:开了一个新线程 // 正确理解:当前线程被释放,任务完成后由某个线程继续
  3. 异步 ≠ 并行

    • 并行:多个任务同时进行(多CPU)
    • 异步:一个线程可以处理多个任务(在等待时切换)

📈 性能对比数据

csharp
// 模拟100个并发请求 public async Task Test() { // 同步:可能需要100个线程 // 异步:可能只需要10-20个线程 // 内存使用: // 同步:每个线程约1MB栈内存 → 100MB // 异步:少量状态机对象 → 约几KB }

✅ 简单判断方法

问自己:"在等待的时候,这个线程能不能去做别的工作?"

  • 能 → 用await(异步)
  • 不能 → 用同步等待

希望这个解释让你明白了await和同步等待的本质区别!

本文作者:张京

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!