
























这篇只讲一个知识点:在 .NET 代码里用 .Result(或 GetAwaiter().GetResult())同步阻塞异步任务,为什么在不同框架下会触发不同类型的事故。
同样一行代码,在两个系统里出现了完全不同的故障:
两边都有这段写法:
public string GetData()
{
return GetDataAsync().Result;
}
private async Task<string> GetDataAsync()
{
await Task.Delay(50);
return "ok";
}
这类框架默认要求 continuation 回到原上下文(UI 线程或请求上下文)。
.Result 先把当前线程阻塞住,Task 完成后 continuation 又想回到这条线程,结果互相等待:
.Result 处阻塞所以你会看到"请求一直转圈"或"界面完全卡死"。
在默认配置下,ASP.NET Core 没有传统的请求级 SynchronizationContext,所以通常不会触发上面的经典互锁。
它会把线程池工作线程同步阻塞住。并发一上来,越来越多线程被卡在 .Result,线程池来不及补充,新请求拿不到线程,就出现线程饥饿:
这就是"看起来不像死锁,但系统几乎不可用"的典型表现。
public sealed class DemoService
{
// ❌ 错误:同步包装异步
public int GetNumber()
{
return GetNumberAsync().Result;
}
// ✅ 正确:异步到底
public async Task<int> GetNumberAsync()
{
await Task.Delay(10);
return 42;
}
}
.Result / .Wait() / GetAwaiter().GetResult()。async,不要做"同步方法包异步"。.Result 在老框架里更容易直接死锁,在 ASP.NET Core 里更容易演化成线程池饥饿;表现不同,本质相同,都是"阻塞等待异步"导致的。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。