GetAwaiter().GetResult() 是最常踩的坑,因它看似同步实则阻塞线程且易致死锁,尤其在 UI 或 ASP.NET 同步上下文中;不释放上下文、不支持取消、异常难追踪,仅适用于无同步上下文的控制台主函数或单元测试。
GetAwaiter().GetResult() 是最常踩的坑它看起来像同步调用,实际会阻塞当前线程并可能引发死锁——尤其在 UI 线程或 ASP.NET 同步上下文里。比如在 WinForms 事件处理器中写 var result = SomeAsyncMethod().GetAwaiter().GetResult();,UI 线程被占住,而异步操作内部又尝试回调到该线程,直接卡死。
.Result 行为一致,但后者还可能包装 AggregateException,更难调试async void 为什么比 async Task 更危险async void 方法无法被等待,异常会直接抛给 SynchronizationContext,导致进程崩溃或静默丢失;而 ASP.NET Core 默认没有全局异常处理器捕获这类异常。
Task 或 Task,返回 void 会绕过框架的请求生命周期管理Button.Click += async void (...) {...})在 WebAssembly 或 Blazor Server 中同样不可靠Task(真有这需求时)绝大多数情况不该这么做。但如

var task = SomeAsyncMethod(); task.Wait(); // 比 GetResult() 稍好:异常原样抛出,不包 AggregateException // 或更稳妥: task.ConfigureAwait(false).GetAwaiter().GetResult(); // 显式放弃上下文捕获
ConfigureAwait(false) 是关键,它告诉编译器“别试图回到原始上下文”,避免死锁try/catch 处理 TaskCanceledException 和业务异常DoSomething() + DoSomethingAsync()),这会把阻塞风险传递给调用方Task.Run(() => { ... }).Result 真的安全吗?不安全。它只是把同步阻塞移到了线程池线程上,看似没卡主线程,实则浪费线程资源、增加调度开销,并可能引发线程池饥饿——尤其高并发场景下大量 Task.Run(...).Result 会拖垮整个应用吞吐。
Task.Run + await,而不是 .Result
await 就自动解决。