async/await 是协作式异步模型,用于不阻塞线程、提升响应性;只对真正异步的 I/O 操作使用,避免滥用在 CPU 密集型或同步代码上,注意返回类型、禁止 async void(除事件处理器)、禁用 .Result/.Wait() 防死锁,并合理处理并发与异常。

async/await 不是“让方法变快”的魔法开关,而是用来不阻塞线程、提升响应性和资源利用率的协作式异步模型。正确使用的关键在于理解“什么该异步”“谁在等待”“线程上下文怎么流转”。
只对真正异步的操作用 async/await
不是所有耗时操作都适合加 async。CPU 密集型任务(比如大数组排序、图像处理)用 Task.Run() 搬到线程池即可,不要盲目套 await;而 I/O 类操作(HTTP 请求、文件读写、数据库查询)天然支持异步,应优先使用它们的 Async 版本(如 HttpClient.GetAsync()、FileStream.ReadAsync())。
- ✅ 正确:调用
await httpClient.GetStringAsync(url) - ❌ 错误:给普通 for 循环包一层
async Task还 await 它 - ⚠️ 谨慎:CPU 工作用
await Task.Run(() => HeavyCalc()),但要评估是否真有必要——可能直接同步执行更高效
async 方法必须有 await,且返回 Task 或 Task
标记为 async 的方法,编译器会重写为状态机。如果里面没写 await,不仅失去异步意义,还会产生不必要的开销(装箱、状态机分配)。返回类型也必须匹配:
- 无返回值 →
async Task(不是void,除非是事件处理器) - 有返回值 →
async Task - ❌ 避免
async void(除 UI 事件),它无法被 await、异常会直接崩掉线程
避免 .Result 和 .Wait(),防止死锁
在有同步上下文的环境(如 WinForms/WPF 主线程、ASP.NET 同步上下文旧版本),直接调用 task.Result 或 task.Wait() 极易引发死锁——因为 await 默认会尝试回到原上下文,而主线程正卡在等结果。
- ✅ 始终用
await task - ✅ 如果真要同步等待(极少见),用
task.ConfigureAwait(false).GetAwaiter().GetResult()放弃上下文捕获 - ❌ 不要混用:在 async 方法里又用 .Result
合理控制并发和异常处理
异步不是“放任不管”。多个异步操作并行时,注意资源竞争和异常聚合:
-
并发请求用
Task.WhenAll(tasks),别用循环 await(那是串行) - 异常会被包装进
AggregateException,建议用try/catch包住 await 表达式,直接捕获业务异常 - 需要取消时,传入
CancellationToken并在 async 方法中适时检查或传给底层 API(如HttpClient.GetAsync(url, token))
基本上就这些。async/await 本身不复杂,但容易忽略上下文、错误类型和调用链一致性。写完多问一句:这个 await 真的释放了线程吗?异常能被正确捕获吗?上层有没有在等它?











