async/await 是异步编程模型,非多线程,核心是避免 IO 等待阻塞主线程且保持同步写法;必须返回 Task 或 Task,禁用 void(除 UI 事件);await 不开新线程,仅挂起方法并交出控制权;需确保调用链全异步,禁止在构造函数等处使用;ConfigureAwait(false) 防死锁,适用于类库与后台服务。

async/await 不是多线程,也不是“自动提速”的魔法——它只是让 IO 等待不卡主线程,且代码写起来像同步一样直白。
async 方法必须返回 Task 或 Task,别用 void(除非 UI 事件处理)
返回 void 的 async 方法无法被 await,也无法传播异常,调试时会静默失败。只在 Windows Forms/WPF 的事件处理器中允许使用,例如:private async void button1_Click(...)。其他所有场景,一律用 Task 或 Task:
-
Task:表示“有异步操作,但不返回值”(如保存日志、发送通知) -
Task:表示“操作完成后返回一个int”,函数体内直接return 42;,编译器自动包装成Task.FromResult(42) - 千万别写
async Task MyMethod() { await SomeAsync(); return; }—— 返回类型是Task就够了,return;是冗余的
await 不会阻塞线程,但它会让方法“暂停并交出控制权”
当你写 await client.GetStringAsync(url),当前方法立即返回(比如返回一个未完成的 Task),调用方可以继续执行;等网络响应回来,后续代码才在合适的上下文(如 UI 线程或线程池线程)中恢复。关键点:
- await 后面的对象必须是“可等待的”(实现
GetAwaiter(),通常是Task或ValueTask) - 如果 await 的是已结束的 Task(比如
Task.FromResult("done")),不会真正暂停,直接往下走 - await 不等于“新开线程”——HttpClient 的请求本身是 IO 完成端口驱动的,不占线程池线程
别在同步方法里“假装 await”:没有 async 就不能用 await
下面这段代码会编译报错:CS4032:The 'await' operator can only be used within an async method:
享有盛誉的PHP高级教程,Zend Framework核心开发人员力作,深入设计模式、PHP标准库和JSON 。 今天,PHP已经是无可争议的Web开发主流语言。PHP 5以后,它的面向对象特性也足以与Java和C#相抗衡。然而,讲述PHP高级特性的资料一直缺乏,大大影响了PHP语言的深入应用。 本书填补了这一空白。它专门针对有一定经验的PHP程序员,详细讲解了对他们最为重要的主题
public string GetData()
{
var result = await httpClient.GetStringAsync("https://api.example.com"); // ❌ 编译不过
return result;
}
正确做法是向上一层推:调用方也得是 async,形成“异步链条”:
public async TaskGetDataAsync() // ✅ 加 async,改返回 Task { var result = await httpClient.GetStringAsync("https://api.example.com"); // ✅ 合法 return result; } // 调用处也要 await(或 .GetAwaiter().GetResult() —— 仅限测试/极少数阻塞场景) public async Task HandleRequest() { string data = await GetDataAsync(); // ✅ }
- 禁止在构造函数、属性 getter/setter、终结器中用 async/await(语法不允许)
- 不要为了“看起来异步”而强行加
Task.Run(() => SyncMethod())包装 CPU 密集型操作——这反而增加调度开销,应改用Task.Run显式卸载到线程池,且调用方需理解这是真多线程
常见陷阱:ConfigureAwait(false) 和 UI 线程死锁
在类库(如 NuGet 包)或后台服务中,建议对所有 await 加上 .ConfigureAwait(false):
var html = await httpClient.GetStringAsync(url).ConfigureAwait(false);
- 默认
ConfigureAwait(true)会尝试回到“原始上下文”(如 WinForms 的 UI 线程),若该上下文正被同步阻塞(比如调用了.Result或.Wait()),就会死锁 - 类库不应假设调用方上下文,所以一律
false;UI 层(如 WPF 的 ViewModel)若需更新控件,则保留默认(即不写ConfigureAwait),由框架自动调度回 UI 线程 - ASP.NET Core 默认无同步上下文,
ConfigureAwait(false)影响不大,但加上更稳妥
最常被忽略的一点:async/await 的价值不在“快”,而在“不卡”。它解决的是资源等待时的线程空转问题,不是替代算法优化或数据库索引。写错一个 await 可能导致整个请求线程被占住,尤其在高并发 Web API 中——这种问题不会报错,只会悄悄拖慢吞吐量。








