Linux(伺服器程式設計):15---兩種高效的事件處理模式(reactor模式、proactor模式)
江南、董少發表於2020-02-09
前言
- 同步I/O模型通常用於實現Reactor模式
- 非同步I/O模型則用於實現Proactor模式
- 最後我們會使用同步I/O方式模擬出Proactor模式
一、Reactor模式
Reactor模式特點
- 它要求主執行緒(I/O處理單元)只負責監聽檔案描述符上是否有事件發生,有的話就立即將時間通知工作執行緒(邏輯單元)。除此之外,主執行緒不做任何其他實質性的工作
- 讀寫資料,接受新的連線,以及處理客戶請求均在工作執行緒中完成
Reactor模式的工作流程
- ①主執行緒往epoll核心事件表中註冊socket上有資料可讀
- ②主執行緒呼叫epoll_wait等待socket上有資料可讀
- ③當socket上有資料可讀時,epoll_wait通知主執行緒。主執行緒則將socket可讀事件放入請求佇列
- ④睡眠在請求請求佇列上的某個工作執行緒被喚醒,它從socket讀取資料,並處理客戶請求,然後往epoll核心事件表中註冊該socket上的寫就緒時間
- ⑤主執行緒呼叫epoll_wait等到socket可寫
- ⑥當socket可寫時,epoll_wait通知主執行緒。主執行緒將socket可寫事件放入請求佇列
- ⑦睡眠在請求佇列上的某個工作執行緒被喚醒,它向socket上寫入伺服器處理客戶請求的結果
二、Proactor模式
Proactor模式特點
- 與Reactor不同,Proactor模式將所有的I/O操作都交給主執行緒和核心來處理,工作執行緒僅僅負責業務邏輯
Proactor模式的工作流程
- ①主執行緒呼叫aio_read函式向核心註冊socket上讀完成事件,並告訴核心使用者讀緩衝區的位置,以及讀操作完成時如何通知應用程式(這裡以訊號為例)
- ②主執行緒繼續處理其他邏輯
- ③當socket上的資料被讀入使用者緩衝區後,核心將嚮應用程式傳送一個訊號,以通知應用程式資料已經可用
- ④應用程式預先定義好的訊號處理函式選擇一個工作執行緒來處理客戶請求。工作執行緒處理完客戶請求之後,呼叫aio_write函式向核心註冊socket上的寫完成事件,並告訴核心使用者寫緩衝區的位置,以及寫操作完成時如何通知應用程式(這裡以訊號為例)
- ⑤主執行緒繼續處理其他邏輯
- ⑥當使用者緩衝區的資料被寫入socket之後,核心將嚮應用程式傳送一個訊號,以通知應用程式資料已經傳送完畢
- ⑦應用程式預先定義好的訊號處理函式選擇一個工作執行緒來做善後處理,比如決定是否關閉socket
- 在上圖中,連線socket上的讀寫事件是通過aio_read/aio_write向核心註冊的,因此核心將通過訊號來嚮應用程式報告連線socket上的讀寫事件。所以,主執行緒的epoll_wait呼叫僅能用來檢測監聽socket上的連線請求事件,而不能用來檢測連線socket的讀寫事件
三、使用同步I/O模擬Proactor模式
原理:
- 主執行緒執行資料讀寫操作,讀寫完成之後,主執行緒向工作執行緒通知這一“完成事件”。那麼從工作執行緒的角度來看,它們就直接獲得了資料讀寫的結果,接下來要做的只是對讀寫的結果進行邏輯處理
工作流程:
- ①主執行緒往epoll核心事件表中註冊socket上的讀就緒事件
- ②主執行緒呼叫epoll_wait等待socket上有資料可讀
- ③當socket上有資料可讀時,epoll_wait通知主執行緒。主執行緒從socket迴圈讀取資料,直到沒有更多資料可讀,然後將讀取到的資料封裝成一個請求物件並插入請求佇列
- ④睡眠在請求佇列上的某個工作執行緒被喚醒,它獲得請求物件並處理客戶請求,然後往epoll核心事件表中註冊socket上的寫就緒事件
- ⑤主執行緒呼叫epoll_wait等到socket可寫
- ⑥當socket可寫時,epoll_wait通知主執行緒。主執行緒往socket上寫入伺服器處理客戶請求的結果