通俗系列之同步、異步、阻塞和非阻塞

前言

在日常的開發中,經常出現同步、異步、阻塞和非阻塞等概念。有些人搞不清楚什麼代碼是同步,什麼代碼是異步。有些人說我用異步了啊,爲什麼效率還是沒提高呢?也許你是用異步了,但是可能是異步阻塞了。有些人一聽說異步好,就不管三七二十一,所有方法全部改成異步,然後就會產生新的問題。歸根結底還是對同步、異步、阻塞和非阻塞的概念不理解。

那究竟什麼是同步、異步、阻塞和非阻塞呢?我決定嘗試用比較通俗的例子舉例來解釋一下,於是便有了這篇文章。

同步

概念

什麼是同步?同步就是一個任務的完成依賴於另外一個任務,只有被依賴的任務完成後,那麼依賴的任務才能繼續完成。

這是一種非常可靠的任務序列,要麼都成功,要麼都失敗。

就和以前的瀑布式開發一樣,你要做一個項目,一定要嚴格按照需求、分析、設計、編碼、測試的步驟順序執行,如果其中有一個問題卡住了,那麼後邊就無法繼續,如果其中有一個環節是搞錯了,那麼這個項目也就可能死掉了。

同步又分同步阻塞和同步非阻塞,接下來將依次舉例解釋。

同步阻塞

同步阻塞就是調用方在等待另一個任務的結果返回來之前,什麼事也沒做,就是死等,直到返回,而且具體要等待的結果是用來幹嘛的,如果不返回,是不知道的。

舉例:

快到飯點了,老王很餓,來到了一家飯店,老王跟老闆說來份蛋炒飯。

然後老闆就是開始做,而老王就一直站在出飯的窗口等着,期間什麼也沒幹,就是等着,直到老闆把飯做好。

說明:

  • 數據調用方:老王 (主線程)

  • 數據持有方:飯店老闆

  • 要獲得的數據:蛋炒飯

  • 同步:老王等着老闆把飯做好

  • 阻塞:老王在等着,期間什麼也沒做

這種調用方式很常見,這是以前普遍的方式,流水線式調用。

如果老王就這樣等着,那麼效率是很低的。

同步非阻塞

同步非阻塞就是,你在等一件事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(承諾)。既然叫承諾,那就很好理解了,我承諾會完成,但不是立即完成。

就比如開會的時候領導說,接下來安排一下任務:

  1. 張三,你把A項目的方案寫一下,寫完跟我講一下,我們一起開會討論一下,張三說”好的“。此時張三隻是給領導了一個承諾,並沒有立即完成。(異步
  2. 李四,你等會把B項目部署一下,看看運行效果。李四說”好的“。此時李四也是一個承諾,並沒有完成B項目部署。(異步
  3. 然後領導將會議記錄和任務安排發了給大家。(同步)

上邊的3並不依賴於1、2,領導不需要非要等張三把A項目方案寫好,也不需要等李四把B項目部署好,再將會議記錄和任務安排發給大家。


如果您覺得這篇文章有幫助到你,歡迎推薦。如果您覺得哪裏不對,也歡迎大家拍磚討論。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章