摘要
同步與異步是IO操作與CPU指令協(xié)作方式的不同
同步sync
1.阻塞blocking:同一條線程獨自完成IO操作和CPU指令操作伸但,全程阻塞
2.非阻塞non-blocking:系統(tǒng)內(nèi)核封裝了檢查IO的執(zhí)行線程籽御,等資源加載完后通知應(yīng)用程序線程進行操作處理,雖然不用等待資源下載枉阵,但本質(zhì)上還是阻塞,只是阻塞的位置變了预茄。
ps:無論是阻塞還是非阻塞兴溜,io讀寫和cpu執(zhí)行指令是耦合的,系統(tǒng)內(nèi)核拷貝數(shù)據(jù)到程序進程的過程還是阻塞的耻陕,這樣做可以保證執(zhí)行順序拙徽,所以在阻塞的情況下CPU滿載不一定是CPU的鍋,很大可能在于IO
異步async
解耦了IO操作和CPU執(zhí)行指令诗宣,IO操作和CPU執(zhí)行指令都暴露出去膘怕,讓程序控制, cpu不去參與IO的事情召庞,IO處理完回調(diào)給應(yīng)用程序便可岛心,數(shù)據(jù)交互無需拷貝一份到程序進程,通過內(nèi)存映射的方式避免了CPU的參與篮灼,但這樣做執(zhí)行順序無法控制忘古!
就因為IO CPU各做各的事情,二者都能充分利用起來诅诱,異步系統(tǒng)下很清晰的可以看到并發(fā)的瓶頸是在IO還是在CPU髓堪,可以及時做出相應(yīng)決策。
.net core/.net 異步區(qū)別
為什么我們常聽說要使用異步就要重頭異步到底,不然性能可能更差干旁,這句話其實是針對.NetFramework的驶沼。
.net下的異步
在.NetFramework中由于SynchronizationContext的存在,await返回必然會回歸主線程
舉個例子
public int Test()
{
return GetAsync()
}
public async Task<int> GetAsync()
{
return 1;
}
同步下執(zhí)行Test()方法的是主線程争群,但在Test()里調(diào)用了異步方法GetAsync()回怜,系統(tǒng)就會分派另一條線程去運行,這時候主線程就可以去搞其他任務(wù)了祭阀,當(dāng)GetAsync()任務(wù)執(zhí)行完畢時候鹉戚,系統(tǒng)會通過SynchronizationContext把主線程回調(diào)回來,接回Test()繼續(xù)執(zhí)行下去专控,但假設(shè)這時候主線程正在處理一個很耗時的任務(wù)抹凳,那回調(diào)就會發(fā)生阻塞,直至主線程完事才回調(diào)完成伦腐,程序才能繼續(xù)走下去赢底,這個過程是非常影響性能的。
ps:如果你希望 GetAsync() 執(zhí)行完后不需要等主線程柏蘑,你就需要配置ConfigureAwait(false)幸冻,這樣就等于告訴系統(tǒng),我不需要原來的主線程來干活咳焚,隨便找個線程給我就行洽损, 但這樣會丟失了主線程的一些信息(HttpContext/Language/TimeZone)。
public int Test()
{
return GetAsync().ConfigureAwait(false).GetAwaiter().GetResult();
}
.net core下的異步
在.NetCore中已經(jīng)剔除了SynchronizationContext革半,剔除他的主要原因主要是性能和進一步簡化操作
在.NetCore中我們不用繼續(xù)關(guān)心異步同步混用情況下碑定,是否哪里沒有設(shè)置ConfigureAwait(false) 會導(dǎo)致的死鎖問題,因為在.netcore中的async/await 可能在任何線程上執(zhí)行又官,并且可能并行運行延刘!
所以說如果把.net core的代碼放到.net下運行,混用同步異步這塊對于.net 來說可能是個坑六敬!
拓展認(rèn)知
同步異步的幾種模型
阻塞IO模型
非阻塞IO模型
多路復(fù)用IO模型
select/poll/epoll : 監(jiān)聽網(wǎng)絡(luò)文件到達系統(tǒng), 完成數(shù)據(jù)收集后通知recv
信號驅(qū)動模型(也是同步IO)
異步IO