async 方法必须返回 Task 或 Task
不满足这个条件的
async方法在编译时就会报错,比如返回
void(除事件处理器外)或
int。这是最基础也最容易忽略的约束。
async Task用于无返回值的异步操作,如
async Task SaveAsync()
async Task<string></string>用于有返回值的异步操作,调用后可用
await获取
string结果 避免写
async void DoSomething()—— 无法被等待、异常会直接崩掉线程、难以测试
await 只能在 async 方法内部使用
试图在普通方法里写
await SomeAsyncMethod()会触发编译错误 CS4032:“The 'await' operator can only be used within an async method.” 不是“加个
async就行”,还要同步改返回类型(见上一条) 如果调用链顶端是 UI 事件(如按钮点击),可合法用
async void,但仅限这一层 Web API 控制器中应始终用
async Task<iactionresult></iactionresult>,而非
async void
不要用 .Result 或 .Wait() 阻塞异步方法
在 ASP.NET Core 或 WinForms 中调用
task.Result或
task.Wait()极易引发死锁,尤其在有同步上下文(SynchronizationContext)的环境里。 典型死锁现象:UI 线程卡住 / HTTP 请求永远不返回 / 日志停在某一行不动 替代方案:把调用点也改成
async+
await,让整个调用链异步穿透 实在无法改(如老代码或第三方同步接口),可用
Task.Run(() => SomeAsyncMethod()).Result绕过上下文,但这是权宜之计,不是设计选择
ConfigureAwait(false) 在类库中值得默认添加
在非 UI、非 ASP.NET 传统管线(如 .NET Core Web API、类库、工具函数)中,
await task.ConfigureAwait(false)能避免不必要的上下文捕获,提升性能并防止潜在死锁。 ASP.NET Core 默认无 SynchronizationContext,所以
ConfigureAwait(false)影响不大,但加了也没坏处 在 NuGet 类库项目中建议统一加上,避免下游使用者踩坑 UI 层(WPF/WinForms)需要更新控件时,不能加
ConfigureAwait(false),否则会抛出跨线程访问异常
public async Task<string> FetchDataAsync()
{
var response = await httpClient.GetAsync("https://api.example.com/data")
.ConfigureAwait(false); // 类库中推荐
return await response.Content.ReadAsStringAsync()
.ConfigureAwait(false);
}
异步的本质不是“多线程”,而是“不阻塞当前线程”。很多人一上来就想着开新线程,反而绕远了。真正容易出问题的地方,往往藏在返回类型和上下文切换这两处。 