文章目錄
- Reactor模式
- Proactor模式
- 同步I/O模型模擬Proactor模式
- 兩者的優缺點
- Reactor
- Proactor
同步I/O模型通常用于實現 Reactor
模式,異步I/O模型通常用于實現 Proactor
模式。(不是絕對的,同步I/O也可模擬出 Proactor
模式)
Reactor模式
原理
Reactor
模式要求主線程(I/O處理單元)只負責監聽文件描述符上是否有事件就緒,如果有則將該就緒事件通知給工作線程(邏輯單元)。除此之外主線程不會進行其他實質性的工作,讀寫數據、接收新連接、業務邏輯處理全部在工作線程中完成。
工作流程
這里以 epoll_wait
為例,使用同步I/O模型實現的 Reactor
模式的工作流程如下:
- 主線程往
epoll
內核事件表中注冊socket
上的讀就緒事件。 - 主線程調用
epoll_wait
開始對socket
的讀事件進行監控。 - 如果
socket
讀就緒,epoll_wait
會通知主線程,主線程則將socket
可讀事件(即socket
連接本身) 放入請求隊列中。 - 請求隊列上某個休眠的工作線程被喚醒,此時會從
socket
中讀取數據,并且處理用戶請求,然后往epoll
內核事件表中注冊該socket
的寫就緒事件。 - 主線程繼續調用
epoll_wait
對socket
的寫事件進行監控。 - 當
socket
寫就緒時,epoll_wait
會通知主線程,主線程則將socket
可寫事件(即socket
連接本身) 放入請求隊列中。 - 請求隊列上某個休眠的工作線程被喚醒,將服務器處理客戶請求的結果寫入到
socket
中
工作線程從請求隊列中取出事件后,根據事件類型來決定如何處理事件,所以不會區分 讀工作線程
和 寫工作線程
。
Proactor模式
原理
Proactor模式則是將所有的I/O操作全部交給主線程和內核處理,工作線程僅僅負責業務邏輯。
工作流程
這里以 aio
為例,使用異步I/O模型實現的 Reactor
模式的工作流程如下:
- 主線程調用
aio_read
向內核注冊socket
上的讀完成事件,并且告訴內核用戶讀緩沖區的位置,以及讀操作完成時如何通知應用程序(這里以信號為例)。 - I/O事件交給內核進行異步處理,此時主線程繼續處理其他邏輯(區別于
Reactor
中主線程需要持續監控就緒事件)。 - 當
socket
上的數據已被讀入用戶緩沖區后,內核向應用程序發送一個信號,通知其數據已可用。 - 通過應用程序預先定義好的信號處理函數來選擇一個工作線程以處理客戶請求。
- 工作線程處理完客戶請求之后會調用
aio_write
向內核注冊socket
上的寫就緒事件,并且告訴內核用戶寫緩沖區的位置,以及寫操作完成時如何通知應用程序(仍選擇使用信號)。 - 主線程繼續處理其他邏輯(同2)。
- 當用戶緩沖區的數據被寫入
socket
后,內核向應用程序發送一個信號,通知其數據發送完成。 - 通過應用程序 注冊(預先定義好) 的信號處理 事件(函數) 來選擇一個工作線程來進行善后處理,例如是否關閉
socket
。
由于讀/寫事件是通過 aio_read/aio_write
向內核中進行 注冊 的,并由內核通過 信號 向應用程序 報告 的。因此,不同于 Reactor
模式,Proactor
的 epoll_wait
僅僅用來監聽 socket
上是否有新的連接請求到來,而不用于 注冊 or 報告 讀/寫事件。
同步I/O模型模擬Proactor模式
原理
主線程執行數據讀寫操作,完成后向工作線程通知事件的完成。從工作線程的角度來看,他們就直接獲得了數據讀寫的結果,接下來的工作就只需要對讀寫結果進行業務邏輯處理。
工作流程
這里以 epoll_wait
為例,使用同步I/O模型實現的 Proactor
模式的工作流程:
- 主線程往
epoll
內核事件表中注冊socket
上的讀就緒事件。(同步I/O注冊就緒事件、異步I/O注冊完成事件) - 主線程調用
epoll_wait
等待socket
上有數據可讀。 - 當
socket
上有數據可讀,epoll_wait
通知主線程,主線程從socket
中循環讀取數據,直到沒有數據可讀。然后將讀到的數據封裝成一個請求對象插入請求隊列中。 - 請求隊列上某個休眠的工作線程被喚醒,此時它會獲取請求對象并且處理客戶請求,然后往
epoll
內核事件表中注冊socket
的寫就緒事件。 - 主線程調用
epoll_wait
等待socket
可寫。 - 如果
socket
寫就緒,epoll_wait
通知主線程,主線程往socket
中寫入服務器處理客戶請求的結果。
兩者的優缺點
Reactor
Reactor
實現了一個被動的事件分離和分發模型:
- 主線程只負責監聽讀寫事件是否就緒,就緒后放入請求隊列,并喚醒請求隊列上某個工作線程;
- 由工作線程讀寫數據并處理客戶請求。
優點:
- 實現相對簡單,對于耗時短的處理場景處理高效。
- 操作系統可以在多個事件源上等待,并且避免了多線程編程相關的性能開銷和編程復雜性。
- 事件的串行化對應用是透明的,可以順序的同步執行而不需要加鎖。
- 將與應用無關的多路分解、分配機制和與應用相關的回調函數分離開來。
缺點:
- 處理耗時長的操作會造成事件分發的阻塞,影響到后續事件的處理。
適用場景:
同時接收多個服務請求,并且依次同步處理它們的事件驅動程序。
Proactor
Proactor
實現了一個主動的事件分離和分發模型:
- 主線程監聽事件是否就緒;
- 內核執行I/O操作讀寫數據;
- 上一步完成后根據預先注冊好的信號函數選擇一個工作線程處理客戶請求。
優點:
- 性能更高,能夠適應耗時長的并發場景(各個任務間互不影響);
- 這種設計允許多個任務并發的執行,從而提高吞吐量。
缺點:
- 實現邏輯復雜,依賴操作系統對異步的支持,目前實現了純異步操作的操作系統少。
- 實現優秀的如
windows IOCP
,但由于windows
系統用于服務器的局限性,目前應用范圍較小。 - 而
Unix/Linux
系統對純異步的支持有限,應用事件驅動的主流方案還是通過select/epoll
來實現。
- 實現優秀的如
適用場景:
異步接收、同時處理多個服務請求的事件驅動程序。