.NET?.Result?不同框架下的死鎖與線程池饑餓問題解析
這篇只講一個(gè)知識(shí)點(diǎn):在 .NET 代碼里用 .Result(或 GetAwaiter().GetResult())同步阻塞異步任務(wù),為什么在不同框架下會(huì)觸發(fā)不同類型的事故。
問題背景
同樣一行代碼,在兩個(gè)系統(tǒng)里出現(xiàn)了完全不同的故障:
- 老系統(tǒng)(ASP.NET MVC 5)請(qǐng)求直接卡死,不返回
- 新系統(tǒng)(ASP.NET Core)不是直接死鎖,而是高峰期吞吐突然掉到很低,請(qǐng)求排隊(duì)超時(shí)
兩邊都有這段寫法:
public string GetData()
{
return GetDataAsync().Result;
}
private async Task<string> GetDataAsync()
{
await Task.Delay(50);
return "ok";
}原理:同一個(gè)坑,兩種后果
場景 1:ASP.NET Classic / WinForms / WPF(有 SynchronizationContext)
這類框架默認(rèn)要求 continuation 回到原上下文(UI 線程或請(qǐng)求上下文)。
.Result 先把當(dāng)前線程阻塞住,Task 完成后 continuation 又想回到這條線程,結(jié)果互相等待:
- 當(dāng)前線程在
.Result處阻塞 - continuation 需要回到當(dāng)前線程繼續(xù)執(zhí)行
- 當(dāng)前線程被阻塞,continuation 進(jìn)不來
- 死鎖
所以你會(huì)看到"請(qǐng)求一直轉(zhuǎn)圈"或"界面完全卡死"。
場景 2:ASP.NET Core(默認(rèn)無 SynchronizationContext)
在默認(rèn)配置下,ASP.NET Core 沒有傳統(tǒng)的請(qǐng)求級(jí) SynchronizationContext,所以通常不會(huì)觸發(fā)上面的經(jīng)典互鎖。
它會(huì)把線程池工作線程同步阻塞住。并發(fā)一上來,越來越多線程被卡在 .Result,線程池來不及補(bǔ)充,新請(qǐng)求拿不到線程,就出現(xiàn)線程饑餓:
- CPU 不一定高
- 數(shù)據(jù)庫不一定慢
- 但接口耗時(shí)和超時(shí)數(shù)暴漲
這就是"看起來不像死鎖,但系統(tǒng)幾乎不可用"的典型表現(xiàn)。
最小對(duì)照示例
public sealed class DemoService
{
// ? 錯(cuò)誤:同步包裝異步
public int GetNumber()
{
return GetNumberAsync().Result;
}
// ? 正確:異步到底
public async Task<int> GetNumberAsync()
{
await Task.Delay(10);
return 42;
}
}如何避坑(只保留最關(guān)鍵三條)
- 不要在任何業(yè)務(wù)調(diào)用鏈上使用
.Result/.Wait()/GetAwaiter().GetResult()。 - API、Service、Repository 全鏈路改成
async,不要做"同步方法包異步"。 - 如果歷史包袱必須保留同步簽名,就讓邊界層同步,內(nèi)部仍然異步,避免層層阻塞傳染。
一句結(jié)論
.Result 在老框架里更容易直接死鎖,在 ASP.NET Core 里更容易演化成線程池饑餓;表現(xiàn)不同,本質(zhì)相同,都是"阻塞等待異步"導(dǎo)致的。
到此這篇關(guān)于.NET .Result 不同框架下的死鎖與線程池饑餓問題解析的文章就介紹到這了,更多相關(guān).NET .Result 死鎖與線程池饑餓內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用Blazor框架實(shí)現(xiàn)在前端瀏覽器中導(dǎo)入和導(dǎo)出Excel
Blazor?是一個(gè)相對(duì)較新的框架,用于構(gòu)建具有?.NET?強(qiáng)大功能的交互式客戶端?Web?UI,本文主要介紹了如何在?Blazor?應(yīng)用程序中實(shí)現(xiàn)?SpreadJS?利用?.NET?的強(qiáng)大功能完成瀏覽器端的?Excel?導(dǎo)入導(dǎo)出,需要的可以參考一下2023-05-05
使用Spring.Net框架實(shí)現(xiàn)多數(shù)據(jù)庫
這篇文章介紹了Spring.Net框架實(shí)現(xiàn)多數(shù)據(jù)庫的方法,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-03-03
asp.net后臺(tái)動(dòng)態(tài)添加JS文件和css文件的引用實(shí)現(xiàn)方法
這篇文章主要介紹了asp.net后臺(tái)動(dòng)態(tài)添加JS文件和css文件的引用實(shí)現(xiàn)方法,是非常簡單實(shí)用的技巧,需要的朋友可以參考下2014-09-09
ADO.Net對(duì)oracle數(shù)據(jù)庫操作的實(shí)例代碼
ADO.Net對(duì)oracle數(shù)據(jù)庫操作的實(shí)例代碼,需要的朋友可以參考一下2013-06-06
.net調(diào)用JScript腳本及JS url加密解密
.net調(diào)用JScript腳本及JS url加密解密,需要的朋友可以參考一下2013-03-03
如何在ASP.NET Core中使用ViewComponent
這篇文章主要介紹了如何在ASP.NET Core中使用ViewComponent,幫助大家更好的理解和學(xué)習(xí)使用.net技術(shù),感興趣的朋友可以了解下2021-04-04

