天天看點

請解釋:同步/異步?阻塞/非阻塞?I/O多路複用?Reactor模型?

作者:Java熱點

我們在看到異步I/O、非阻塞I/O、I/O多路複用、Reactor模型等等名詞時,往往搞不清楚其中的關系,這裡做一個簡單的梳理。

1. 同步、異步、阻塞、非阻塞

這幾個概念在很多部落格都有講到,但是似乎并不準确,我在看了多篇文章之後,将個人的了解記錄下:

(1) 消息通信次元

從消息通信的次元讨論時,同步和阻塞以及異步和非阻塞是相同概念,但是需要區分發送方和接收方:

阻塞式發送(同步發送):發送方發送消息後會被阻塞,直到消息被接收方接收到;

非阻塞式發送(異步發送):發送方發送消息後,不需要等待消息被接收方接收到,可以直接進行後續邏輯;

阻塞式接收(同步接收):接收方擷取消息時會被阻塞,直到發送方的消息到達;

非阻塞式接收(異步接收):接收方擷取消息後,要麼接收到一個有效的結果,要麼擷取到一個null,不會被阻塞。

上述不同類型的發送方式和接收方式可以自由組合,不代表阻塞式發送就必須對應阻塞式接收,非阻塞式發送就必須對應非阻塞接收。

參考這篇文章。

(2) I/O次元

從I/O次元考慮,同步/異步與阻塞/非阻塞就有所差別了。

從一次網絡I/O的read操作來說,可以把過程分為兩部分:

  1. 等待資料準備

    阻塞:線程一直阻塞等待資料;

    非阻塞:線程發送請求後,不等待資料,通過輪詢/信号量等方式去擷取資料是否準備好了。

  2. 将資料從核心拷貝到使用者空間程序中

    同步:準備好資料後,線程需要自己将資料從核心拷貝到使用者空間,然後處理資料;

    異步:準備好資料後,系統核心把資料拷貝到使用者空間,然後通知相應線程進行資料處理。

用個燒開水的例子來說明:

我開始燒水之後,一直等待着水開,就是阻塞;

我開始燒水之後,去幹别的事情了,過一會回來看一下水有沒有燒開,就是非阻塞;

水開了之後,需要過我過來關火,就是同步;

水開了之後,燒水壺自動斷電,就是異步。

是以,根據同步/異步和阻塞/非阻塞的不同,就引申出了不同的I/O模型。

2. I/O模型

(1) 同步阻塞模型

線程發送請求後,阻塞等待資料(阻塞),資料準備好後,由線程将資料從核心拷貝到使用者空間(同步)。

請解釋:同步/異步?阻塞/非阻塞?I/O多路複用?Reactor模型?

優點:實作簡單,線程阻塞時挂起,不占用CPU資源;

缺點:每個連接配接需要單獨的線程來處理,并發量大時記憶體和上下文切換時的CPU開銷很大。

(2) 同步非阻塞模型

線程發送請求後,不必等待,而是輪詢資料是否準備好(非阻塞),資料準備好後,由線程将資料從核心拷貝到使用者空間(同步)。

請解釋:同步/異步?阻塞/非阻塞?I/O多路複用?Reactor模型?

優點:不會阻塞線程;

缺點:輪詢會不斷消耗CPU資源。

(3) 同步多路複用模型

在I/O多路複用模型中,會用到 Select 或 Poll 函數或 Epoll 函數(Linux 2.6 以後的核心開始支援),這些函數也會使線程阻塞,但是和阻塞 I/O 有所不同。

請解釋:同步/異步?阻塞/非阻塞?I/O多路複用?Reactor模型?

以select方法來說,請求發送之後會注冊socket到select,select輪詢多個socket資料是否準備好(非阻塞),資料準備好後,由線程将資料從核心拷貝到使用者空間(同步)。

優點:可以通過一個阻塞對象,等待多個socket上的資料,即通過一個線程監聽多個I/O,哪個有資料來了,就交給線程來處理;可以避免頻繁地進行阻塞和喚醒操作,減少系統調用的次數,進而大幅降低CPU占用率和記憶體開銷。此外,由于可以同時處理多個I/O事件,還可以提高程式的并發性和吞吐量,适用于高負載和高并發的應用場景。

缺點:需要兩次系統調用,在連接配接數少時性能不是很高。

(4) 同步信号驅動模型

在信号驅動式 I/O 模型中,核心通過信号量辨別資料是否準備好(非阻塞),資料準備好後,由線程将資料從核心拷貝到使用者空間(同步)。

請解釋:同步/異步?阻塞/非阻塞?I/O多路複用?Reactor模型?

優點:線程完全沒有阻塞,可以提高資源使用率;

缺點:也需要兩次系統調用,并且在并發量大時,信号量太多,也會影響性能。

(5) 異步I/O模型

在異步模型中,不再區分阻塞或非阻塞,因為完全由核心去完成等待資料準備以及将資料拷貝到使用者空間的過程,線程會收到資料到達的通知。

請解釋:同步/異步?阻塞/非阻塞?I/O多路複用?Reactor模型?

優點:與同步非阻塞相比,實作簡單;與同步阻塞相比,減少了高并發下的線程數。

缺點:為了實作真正的異步I/O,作業系統需要做大量的工作,例如Linux中的AIO以及Windows中的IOCP。

3. 線程模型

上邊我們介紹了幾種I/O模型,不同的I/O模型肯定對應了不同的線程使用方式,即所說的線程模型:

(1) 傳統阻塞I/O服務模型

請解釋:同步/異步?阻塞/非阻塞?I/O多路複用?Reactor模型?

該線程模型對應阻塞式I/O,每個連接配接都需要獨立的線程來處理,在高并發的場景下會建立大量的線程,資源占用大;如果目前線程暫時沒有資料可讀,那麼就一直阻塞住,造成線程資源浪費。

(2) Reactor模型

針對傳統阻塞I/O的缺點,比較常用的有兩種解決方案:

  1. 基于I/O多路複用模型:多個連接配接共用一個阻塞對象,應用程式隻需要在一個阻塞對象上等待,無需阻塞等待所有連接配接。當某條連接配接有新的資料可以處理時,作業系統通知應用程式,線程從阻塞狀态傳回,開始進行業務處理;
  2. 基于線程池複用線程資源:不需要給每個連接配接建立線程,而是将連接配接完成後的業務處理任務配置設定給線程池中的線程進行處理,一個線程可以處理多個用戶端的業務。

也就是說,I/O多路複用 + 線程池,就是Reactor模型的基本設計思想。其中,I/O多路複用通過阻塞一個線程來實作多個socket的監聽,又通過線程池來實作線程的複用。

請解釋:同步/異步?阻塞/非阻塞?I/O多路複用?Reactor模型?

Reactor模型中有兩個關鍵角色:

Reactor:Reactor 在一個單獨的線程中運作,負責監聽和分發事件,分發給适當的處理程式來對 IO 事件做出反應。 它就像公司的電話接線員,它接聽來自客戶的電話并将線路轉移到适當的聯系人;

Handler:處理程式執行 I/O 事件要完成的實際事件,類似于客戶想要與之交談的公司中的實際官員。Reactor 通過排程适當的處理程式來響應 I/O 事件,處理程式執行非阻塞操作。

也就是說,Reactor就是那個對多個socket進行監聽的線程,并且将事件分發給不同的Handler來處理。

根據 Reactor 的數量和線程池線程數量的不同,又分為 3 種典型的實作:

  1. 單 Reactor 單線程;
  2. 單 Reactor 多線程;
  3. 主從 Reactor 多線程。

(3) Proactor 模型

在Reactor模型中,讀寫操作都需要應用程式同步操作,是以Reactor是同步模型。

如果把I/O操作改為異步,即交給作業系統來完成就能進一步提升性能,就是異步模型Proactor。

請解釋:同步/異步?阻塞/非阻塞?I/O多路複用?Reactor模型?

4. 總結

現在來簡單回答下文章标題中的問題:

同步/異步和阻塞/非阻塞,在不同的次元下的意義不同:

在消息通信的次元下,同步和阻塞以及異步和非阻塞為同義詞,根據發送方和接收方的不同,可以分為阻塞式發送(同步發送)、非阻塞式發送(異步發送)、阻塞式接收(同步接收)和非阻塞式接收(異步接收),不同類型的發送方式和接收方式可以自由組合。

在I/O的次元下,阻塞/非阻塞表示在等待資料準備時線程是否被阻塞挂起,同步/異步表示是否由線程來進行資料從核心到使用者空間的拷貝。

I/O多路複用是一種I/O模型,它通過阻塞一個線程,來實作對多個socket的監聽。其他常見I/O模型還有同步阻塞、同步非阻塞、同步信号驅動以及異步I/O。

Reactor模型是一種同步非阻塞線程模型,它的基本設計思想是I/O多路複用 + 線程池。其中,I/O多路複用通過阻塞一個線程來實作多個socket的監聽,又通過線程池來實作線程的複用。其他還有傳統阻塞式I/O以及Proactor模型(異步模型)。

繼續閱讀