前言
在日常的開發中,經常出現同步、異步、阻塞和非阻塞等概念。有些人搞不清楚什麼代碼是同步,什麼代碼是異步。有些人說我用異步了啊,爲什麼效率還是沒提高呢?也許你是用異步了,但是可能是異步阻塞了。有些人一聽說異步好,就不管三七二十一,所有方法全部改成異步,然後就會產生新的問題。歸根結底還是對同步、異步、阻塞和非阻塞的概念不理解。
那究竟什麼是同步、異步、阻塞和非阻塞呢?我決定嘗試用比較通俗的例子舉例來解釋一下,於是便有了這篇文章。
同步
概念
什麼是同步?同步就是一個任務的完成依賴於另外一個任務,只有被依賴的任務完成後,那麼依賴的任務才能繼續完成。
這是一種非常可靠的任務序列,要麼都成功,要麼都失敗。
就和以前的瀑布式開發一樣,你要做一個項目,一定要嚴格按照需求、分析、設計、編碼、測試的步驟順序執行,如果其中有一個問題卡住了,那麼後邊就無法繼續,如果其中有一個環節是搞錯了,那麼這個項目也就可能死掉了。
同步又分同步阻塞和同步非阻塞,接下來將依次舉例解釋。
同步阻塞
同步阻塞就是調用方在等待另一個任務的結果返回來之前,什麼事也沒做,就是死等,直到返回,而且具體要等待的結果是用來幹嘛的,如果不返回,是不知道的。
舉例:
快到飯點了,老王很餓,來到了一家飯店,老王跟老闆說來份蛋炒飯。
然後老闆就是開始做,而老王就一直站在出飯的窗口等着,期間什麼也沒幹,就是等着,直到老闆把飯做好。
說明:
-
數據調用方:老王 (主線程)
-
數據持有方:飯店老闆
-
要獲得的數據:蛋炒飯
-
同步:老王等着老闆把飯做好
-
阻塞:老王在等着,期間什麼也沒做
這種調用方式很常見,這是以前普遍的方式,流水線式調用。
如果老王就這樣等着,那麼效率是很低的。
同步非阻塞
同步非阻塞就是,你在等一件事A的結果時,可以繼續去幹另外一件事B。但是你需要時不時的回來瞧一瞧A的結果是否返回了。
程序上通常都是用輪詢去獲取A事件是否完成並返回結果。
舉例:
快到飯點了,老王很餓,來到了一家飯店,老王跟老闆說來份蛋炒飯。
然後老闆就是開始做,而老王呢,就跑到飯店的門頭透透氣,順便刷刷微博,但是老王需要時不時的跑進去問問老闆飯好了沒有
說明:
-
數據調用方:老王 (主線程)
-
數據持有方:飯店老闆
-
要獲得的數據:蛋炒飯
-
同步:老王等着老闆把飯做好
-
非阻塞:老王等老闆把飯做好,但是並不是站着死等,而是出門透透氣,刷刷微博
這種調用方式,一般在同步操作中,中間需要處理一個比較耗時的操作時,通常是另外開闢一個線程去處理這個比較耗時的操作,但是後邊需要自己去輪詢另外一個線程的執行結果是否完成。
就比如上邊,飯店老闆做飯比較耗時,老王並沒有一直等着,而是出門刷手機,但是飯有沒有做好他需要時不時的跑進去問問,需要切換自己的位置。
異步
概念
異步就是調用方不需要一直等待被依賴的操作完成,可以繼續幹其他事情,當被依賴的操作完成後,會自動通知調用方。
調用異步操作時,被依賴的操作會直接返回一個任務或承諾給調用方,然後調用方會繼續執行後邊的操作。等被依賴的操作完成後,會通知調用方
日常生活中就很常見,比如:
領導給你安排一個任務,可能會說:小明你把xx項目的方案寫一下,寫完之後xx項目的相關人員一起去會議室開個會。小明肯定不能立即把方案寫完,此時就會回答領導,“好的領導”。然後領導繼續幹自己的事,小明就去寫方案,寫完之後小明去通知領導,然後領導拿到方案後通知相關人員一起開會。
異步阻塞
異步阻塞就是雖然被依賴項完成時會通知調用方,但是調用方並沒有繼續去幹其他事,而是依舊繼續在那等着。
舉例:
快到飯點了,老王很餓,來到了一家飯店,老王跟老闆說來份蛋炒飯。
老闆在點菜機上下了單,並給了老王一個號,告訴老王等飯做好後,會叫號通知的。
雖然會通知,但是老王很餓很着急,依舊站在出飯窗口等着。
說明:
-
數據調用方:老王 (主線程)
-
數據持有方:飯店老闆
-
要獲得的數據:蛋炒飯
-
異步:飯做好了會叫號通知的。
-
阻塞:雖然會通知,但是老王依舊站那等着飯做好,期間什麼也沒做。
目前,現實社會中大部分場景都有了叫號功能,不需要一直排隊等着。但是也免不了有個別人比較着急,即便是會叫號也一直站旁邊等着。
異步非阻塞
異步非阻塞就是被依賴項完成時會通知調用方,調用方無需等待被依賴項,可以繼續幹其他跟依賴項結果無關的事。
舉例:
快到飯點了,老王很餓,來到了一家飯店,老王跟老闆說來份蛋炒飯。
老闆在點菜機上下了單,並給了老王一個單子,單子上是5號,告訴老王飯做好後,會叫這個號通知的。
然後,老王拿着號找了個位置,刷起了新聞,期間還出門打了個電話,處理了一些工作上的事。突然老王聽到叫號機通知5號的飯做好了,然後老王去窗口拿自己的飯。
說明:
-
數據調用方:老王 (主線程)
-
數據持有方:飯店老闆
-
要獲得的數據:蛋炒飯
-
異步:飯做好了會叫號通知的。
-
非阻塞:因爲飯做好後會叫號,所以老王並沒有一直站那等着,期間找了個位置刷起了新聞,還出去接了個電話
這種效率是最高的,你做你的飯,我可以繼續幹不依賴於飯的事。你做好飯了叫我就行。
異步在目前的前後端開發中都挺常見的。
前端中通常會返回一個Promise(承諾)對象,.NET後端通常返回一個Task(任務),兩個其實是一個意思。接下來以前端來舉例例如:
//AService.js 有兩個方法,一個是獲取數據getData(),一個是做一些耗時操作doSomething()
async getData(){
return await GetMyData()//該方法需要查數據比較耗時
}
//該方法就寫一些日誌
async doSomething(){
await writeLog()
}
//B.vue頁面
inint (){
// 1 異步執行某個操作
doSomething()
// 2 異步獲取數據,然後對數據做xxx處理。代碼的閱讀和現實的語義是一樣的。
getData().then(res => {
//可以針對res做相應的處理
})
//3
//... 不依賴於1、2的同步操作。
}
//上邊doSomething()和getData().then()都會立即返回一個Promise(承諾),承諾會完成.
上邊1、2都是瞬間完成的,返回了一個Promise(承諾)。既然叫承諾,那就很好理解了,我承諾會完成,但不是立即完成。
就比如開會的時候領導說,接下來安排一下任務:
- 張三,你把A項目的方案寫一下,寫完跟我講一下,我們一起開會討論一下,張三說”好的“。此時張三隻是給領導了一個承諾,並沒有立即完成。(異步)
- 李四,你等會把B項目部署一下,看看運行效果。李四說”好的“。此時李四也是一個承諾,並沒有完成B項目部署。(異步)
- 然後領導將會議記錄和任務安排發了給大家。(同步)
上邊的3並不依賴於1、2,領導不需要非要等張三把A項目方案寫好,也不需要等李四把B項目部署好,再將會議記錄和任務安排發給大家。
如果您覺得這篇文章有幫助到你,歡迎推薦。如果您覺得哪裏不對,也歡迎大家拍磚討論。