同步與非同步,阻塞與非阻塞基礎

2022-08-26 19:18:13 字數 3492 閱讀 4670

1.同步與非同步

同步和非同步關注的是訊息通訊機制 (synchronous communication/ asynchronous communication)

同步,在發出乙個呼叫時,在沒有得到結果之前,該呼叫就不返回。但是一旦呼叫返回,就得到返回值了。是由呼叫者主動等待這個呼叫的結果。

非同步,呼叫在發出之後,這個呼叫就直接返回了,所以沒有返回結果。當乙個非同步過程呼叫發出後,呼叫者不會立刻得到結果。而是在呼叫發出後,被呼叫者通過狀態、通知來通知呼叫者,或通過**函式處理這個呼叫。

2.阻塞與非阻塞

阻塞和非阻塞關注的是程式在等待呼叫結果(訊息,返回值)時的狀態.

阻塞呼叫:指呼叫結果返回之前,當前執行緒會被掛起。呼叫執行緒只有在得到結果之後才會返回。

非阻塞呼叫:指在不能立刻得到結果之前,該呼叫不會阻塞當前執行緒。

3.通俗理解,**網路

老張愛喝茶,廢話不說,煮開水。

出場人物:老張,水壺兩把(普通水壺,簡稱水壺;會響的水壺,簡稱響水壺)。

1 老張把水壺放到火上,立等水開。(同步阻塞)

老張覺得自己有點傻

2 老張把水壺放到火上,去客廳看電視,時不時去廚房看看水開沒有。(同步非阻塞)

老張還是覺得自己有點傻,於是變高階了,買了把會響笛的那種水壺。水開之後,能大聲發出嘀~~~~的噪音。

3 老張把響水壺放到火上,立等水開。(非同步阻塞)

老張覺得這樣傻等意義不大

4 老張把響水壺放到火上,去客廳看電視,水壺響之前不再去看它了,響了再去拿壺。(非同步非阻塞)

老張覺得自己聰明了。

所謂同步非同步,只是對於水壺而言。

普通水壺,同步;響水壺,非同步。

雖然都能幹活,但響水壺可以在自己完工之後,提示老張水開了。這是普通水壺所不能及的。

同步只能讓呼叫者去輪詢自己(情況2中),造成老張效率的低下。

所謂阻塞非阻塞,僅僅對於老張而言。

立等的老張,阻塞;看電視的老張,非阻塞。

情況1和情況3中老張就是阻塞的,媳婦喊他都不知道。雖然3中響水壺是非同步的,可對於立等的老張沒有太大的意義。

所以一般非同步是配合非阻塞使用的,這樣才能發揮非同步的效用。

在處理 io 的時候,阻塞和非阻塞都是同步 io。只有使用了特殊的 api 才是非同步 io。

幾種常見的網路程式設計io模型

阻塞式i/o模型:預設情況下,所有套接字都是阻塞的。怎麼理解?先理解這麼個流程,乙個輸入操作通常包括兩個不同階段:

(1)等待資料準備好;

(2)從核心向程序複製資料。

對於乙個套接字上的輸入操作,第一步通常涉及等待資料從網路中到達。當所有等待分組到達時,它被複製到核心中的某個緩衝區。第二步就是把資料從核心緩衝區複製到應用程式緩衝區。

非阻塞式i/o: 以下這句話很重要:程序把乙個套接字設定成非阻塞是在通知核心,當所請求的i/o操作非得把本程序投入睡眠才能完成時,不要把程序投入睡眠,而是返回乙個錯誤。看看非阻塞的套接字的recvfrom操作,可以看出recvfrom總是立即返回。

i/o多路復用:雖然i/o多路復用的函式也是阻塞的,但是其與以上兩種還是有不同的,i/o多路復用是阻塞在select,epoll這樣的系統呼叫之上,而沒有阻塞在真正的i/o系統呼叫如recvfrom之上。

訊號驅動式i/o:用的很少。

非同步i/o:這類函式的工作機制是告知核心啟動某個操作,並讓核心在整個操作(包括將資料從核心拷貝到使用者空間)完成後通知我們。

其實前四種i/o模型都是同步i/o操作,他們的區別在於第一階段,而他們的第二階段是一樣的:在資料從核心複製到應用緩衝區期間(使用者空間),程序阻塞於recvfrom呼叫。相反,非同步i/o模型在這兩個階段都要處理。

linux 下tcp socket為例:

阻塞就是 recv/read的時候 socket接收緩衝區要是有資料就讀, 沒資料我就一直睡覺賴著不走,直到有資料來了讀完我才走。send/write的時候,要是傳送緩衝區滿了,沒有空間繼續傳送了我也一直睡覺賴著不走,直到傳送緩衝區騰出足夠的空間讓我把資料全部塞到傳送緩衝區裡我才走。(當然如果你通過setsockopt設定了讀寫超時,超時時間到了還是會返回-1和eagain,不再睡覺等待)

非阻塞就是recv/read的時候,要是接收緩衝區有資料我就讀完,沒有資料我直接帶著返回的-1和egain走人,絕不睡覺等待耽誤時間。write/send的時候, 要是傳送緩衝區有足夠的空間,就立刻把資料塞到傳送緩衝區去,然後走人,如果傳送快取區滿了,空間不足,那直接帶著返回的-1和eagain走人。

至於io多路復用,首先要理解的是,作業系統為你提供了乙個功能,當你的某個socket接收快取區有資料可讀,或者傳送緩衝區有空間可寫的時候,它可以給你乙個通知。這樣當配合非阻塞的socket使用時,只有當系統通知我哪個描述符可讀了,我才去執行read操作,可以保證每次read都能讀到有效資料而不做純返回-1和eagain的無用功。寫操作類似。作業系統的這個功能通過select/poll/epoll之類的系統呼叫函式來使用,這些函式都可以同時監視多個描述符的讀寫就緒狀況,這樣,多個描述符的i/o操作都能在乙個執行緒內完成,這就叫i/o多路復用,這裡的「復用」指的是復用同乙個執行緒。

至於事件驅動,其實是i/o多路復用的乙個另外的稱呼。

至於非同步同步,我們常見的linux下的網路程式設計模型大部分都是同步io,以讀操作為例,本質上都是需要使用者呼叫read/recv去從核心緩衝區把資料讀完再處理業務邏輯。非同步io則是核心已經把資料讀好了,使用者直接處理邏輯。非同步io在linux下一般是用aio庫。

層次進行討論:

同步、非同步、阻塞、非阻塞,是分3個層次的:

cpu層次;

執行緒層次;

程式設計師感知層次。

這幾個概念之所以容易混淆,是因為沒有分清楚是在哪個層次進行討論。

cpu層次

在cpu層次,或者說作業系統進行io和任務排程的層次,現代作業系統通常使用非同步非阻塞方式進行io(有少部分io可能會使用同步非阻塞輪詢),即發出io請求之後,並不等待io操作完成,而是繼續執行下面的指令(非阻塞),io操作和cpu指令互不干擾(非同步),最後通過中斷的方式來通知io操作完成結果。

執行緒層次多執行緒(同步阻塞);

io多路復用(select,poll,epoll)(同步非阻塞,嚴格地來講,是把阻塞點改變了位置);

直接暴露出非同步的io介面,如kernel-aio和iocp(非同步非阻塞)。

程式設計師感知層次

在linux中,上面提到的第2種思路用得比較廣泛,也是比較理想的解決方案。然而,直接使用select之類的介面,依然比較複雜,所以各種庫和框架百花齊放,都試圖對io多路復用進行封裝。此時,庫和框架提供的api又可以選擇是以同步的方式還是非同步的方式來展現。如python的asyncio庫中,就通過協程,提供了同步阻塞式的api;如node.js中,就通過**函式,提供了非同步非阻塞式的api。

總結

同步與非同步 阻塞與非阻塞

設想一下這個情景,你列了個購物清單 a,b,c,d 去商店買東西,你向老闆詢店問有沒有你想要的a i o操作 於是老闆去倉庫檢視,這個時候假如你在前台等待老闆查詢結果回來那麼這種詢問等待的方式為同步,此時你相當於被掛起,是處於阻塞狀態的,而如果你沒有在前台等待老闆的查詢而是接著進店逛看有沒有你想要的...

同步與非同步,阻塞與非阻塞

同步與非同步阻塞與非阻塞 阻塞和非阻塞關注的是程式在等待呼叫結果 訊息,返回值 時的狀態.阻塞呼叫是指呼叫結果返回之前,當前執行緒會被掛起。呼叫執行緒只有在得到結果之後才會返回。非阻塞呼叫指在不能立刻得到結果之前,該呼叫不會阻塞當前執行緒。還是上面的例子,你打 問書店老闆有沒有 分布式系統 這本書,...

同步與非同步 阻塞與非阻塞

摘要 一直為同步非同步,阻塞非阻塞概念所困擾,特定總結了下,原來是這麼個意思 一直為同步非同步,阻塞非阻塞概念所困擾,特定總結了下 1 概念介紹 2 訊息通知ps 呼叫者獲取依賴服務非同步 結果一般有兩種方式 一種是主動去輪訓查詢非同步 的結果,一種呼叫依賴服務時傳入乙個callback方法或者 位...