天天看點

阻塞/非阻塞/同步/異步詳解

歡迎轉載,轉載請注明原文位址:http://blog.csdn.net/majianfei1023/article/details/45314803

零.導論

阻塞是不是同步,非阻塞是不是異步,他們是什麼關系?剛接觸網絡程式設計的同學經常會問這種問題,而且把他們搞混,在這裡我用我自己的了解詳細解答一下。

想了半天,決定先講阻塞和非阻塞,為什麼呢,因為他們的時序在前面(至少我是這麼了解的)。

我們以一句總結開篇,再以另一句總結結束吧。

阻塞IO和非阻塞IO的差別就在于:阻塞/非阻塞, 它們是程式在等待消息(調用函數)時的狀态,應用程式的調用是否立即傳回!

同步IO和異步IO的差別就在于:同步/異步, 它們是消息的通知機制,同步IO是調用完成之後傳回來通知的,異步是調用完成之後不等傳回,自己做自己的事,然後消息完成之後自然會有它的方式(狀态、通知、回調等)來通知它。

是以,他們關注的點是不一樣的,阻塞和非阻塞關注的是調用是否立即傳回,同步和異步關注的是消息怎麼通知給程式的。

一、阻塞與非阻塞

阻塞/非阻塞, 它們是程式在等待消息(無所謂同步或者異步)時的狀态.

1. 阻塞I/O模型:

阻塞調用是指調用結果傳回之前,目前線程會被挂起。函數隻有在得到結果之後才會傳回。

下面我用 UNP1的圖來進行講解:

阻塞/非阻塞/同步/異步詳解

如圖6.1:

程序調用recvfrom向核心請求資料,核心資料并沒有準備好時,不傳回給程序。這時程序就阻塞在recvfrom系統調用以等待資料就緒。

阻塞函數在完成其指定的任務以前不允許程式調用另一個函數。例如,程式執行一個讀資料的函數調用時,在此函數完成讀操作以前将不會執行下一程式語句。當伺服器運作到recvfrom語句時,而沒有客戶連接配接服務請求到來,伺服器就會停止在recvfrom語句上等待連接配接服務請求的到來。這種情況稱為阻塞(blocking)。阻塞的時候cpu不會給線程配置設定時間片,即線程暫停運作,但CPU可以給其他線程(或者程序)使用,并不會浪費CPU性能。

當資料沒有準備好,沒有傳回成功(或失敗)的訓示之前,程式卡在recvfrom調用而不會繼續往下走。直到調用傳回成功(或失敗)。

2.非阻塞I/O模型:

非阻塞和阻塞的概念相對應,指在不能立刻得到結果之前,該函數不會阻塞目前線程,而會立刻傳回。

阻塞/非阻塞/同步/異步詳解

如圖6.2:

當核心中沒有資料準備好時,核心立即傳回一個EWOULDBLOCK錯誤給應用程式,應用程式不需要把本程序投入睡眠。這樣應用程式(程序)就需要不停的系統調用,也就是輪詢(polling)。應用程式持續輪詢核心,以檢視資料是否就緒。這麼做往往耗費大量CPU時間(當然你隻是想檢視資料有沒有準備好,沒準備輪詢的話,那就不會了)。

看到上面兩個圖有什麼差別嗎?在等待資料階段,

果然看圖比我巴拉巴拉講一大堆有用得多,我們仔細看,系統調用recvfrom分為兩個階段,

1.等待資料準備好的階段(這個決定了是阻塞還是非阻塞);

2.講資料從核心拷貝到使用者空間的階段(這個我們稍後講,它決定了是同步還是異步)。

從圖可以看出,阻塞和非阻塞的差別主要在于等待資料的時候是等待資料就緒還是立即傳回,

是以:阻塞/非阻塞, 它們是程式在等待消息(無所謂同步或者異步)時的狀态。(阻塞就是一直阻塞到有資料準備就緒,非阻塞就是立即傳回)。

二、同步與異步

同步/異步, 它們是消息的通知機制:

1.同步IO模型:

所謂同步,就是在發出一個功能調用時,在沒有得到結果之前,該調用就不傳回。

2.異步IO模型:

當一個異步過程調用發出後,調用者不會立刻得到結果。

實際處理這個調用的部件是在調用發出後,

通過狀态、通知來通知調用者,或通過回調函數處理這個調用。

(也就是說,阻塞關注的是立即傳回還是稍後傳回的話,異步傳回都不需要,他不關注這個,處理完會自動通知他)

我們繼續看 圖 6.1和圖6.2,之前研究阻塞和非阻塞的時候,隻顧研究他們的不同了,那麼現在我們研究一下他們的相同點:看下半部分,将資料從核心複制到使用者空間的時候,等到複制完成,傳回給程序,程序繼續進行。

那麼我們再看一張圖:

阻塞/非阻塞/同步/異步詳解

如圖6.5,這是異步I/O模型,不用我說,大家都看到了。那麼這個跟上面兩張圖的相同點(即:将資料從核心複制到使用者空間,然後程序繼續執行)有什麼差別呢?

真相隻有一個:

程序調用後就繼續執行了(也就是我們說的,它不關注返不傳回),不等資料複制到使用者空間,程序繼續執行了,那麼資料複制到使用者空間之後怎麼告訴程序呢?反正核心有辦法通知你資料已經處理好了,你可以選擇處理了。

這個是異步,那上面自然是同步了,我們總結一下,同步和異步的差別:

同步I/O操作:導緻請求程序阻塞,直到I/O操作完成(你沒完成,我就看着你做,然後什麼也不做,等你做完了,把東西給我);

異步I/O操作:不導緻請求程序阻塞。而當你完成拷貝後,想辦法通知程序就是了,它才懶得等你(是以說,異步I/O是很傲嬌的,我是老大,我憑什麼等你,我先做其他事,等你處理好了,打個電話告訴我)

阻塞IO和非阻塞IO的差別就在于:應用程式的調用是否立即傳回!

同步IO和異步IO的差別就在于:資料拷貝的時候程序是否阻塞!

是以,簡單地說,阻塞和非阻塞是指等待資料階段,調用是否立即傳回,而同步異步則是指的,複制資料的時候,我是阻塞在這裡等你傳回,還是我先忙其他的,等你處理好了,告訴我一聲。

我再繼續拿張圖總結一下:

阻塞/非阻塞/同步/異步詳解

如果我們要觀察 阻塞和非阻塞,觀察 1和2就可以了:

程式發出調用後是否立即傳回。

如果我們要觀察同步和異步,觀察3和4就可以了:

函數完成時程序是否阻塞(是被動等待傳回,還是你主動通知)。

第一列是:同步阻塞I/O模型

第二列是:同步非阻塞I/O模型

第五列是:異步I/O模型。

對unix來講:阻塞式I/O(預設),非阻塞式I/O(nonblock),I/O複用(select/poll/epoll)都屬于同步I/O,因為它們在資料由核心空間複制回程序緩沖區時都是阻塞的(不能幹别的事)。隻有異步I/O模型(AIO)是符合異步I/O操作的含義的,即在1資料準備完成、2由核心空間拷貝回緩沖區後 通知程序,在等待通知的這段時間裡可以幹别的事。