非同步與高效能輸入輸出

反應器與前攝器模式(reactor / proactor)

/ reactor -> ree-AK-tor; proactor -> proh-AK-tor /

這是兩個用以建構「以少數執行緒處理眾多連線」之伺服器的經典設計模式,而它們正好對應就緒模型與完成模型。反應器對「就緒」作出反應:「這個通訊端現在可讀」,接著你的處理常式去做讀取。前攝器則「提前行動」:它啟動 I/O,由作業系統完成整個操作,然後帶著已到手的結果呼叫你的處理常式。

反應器模式,逐步來看:一個同步的事件解多工器(epoll、kqueue、select)等待許多描述符;當其中一些就緒時,它把每個派發給已登記的事件處理常式;處理常式自己執行實際的非阻塞讀或寫,然後返回。經典形態就是事件迴圈加上一張回呼表。它是就緒式的,所以搬資料的是處理常式。前攝器模式,逐步來看:你提交一個非同步操作(讀進這個緩衝區);作業系統在背景執行讀取;一個完成解多工器(IOCP、io_uring 的完成佇列)等待已完成的操作;當一個完成時,它派發給一個完成處理常式,後者收到的是已經填好的緩衝區。它是完成式的,所以在你的處理常式跑之前,作業系統已搬好資料。

為何重要:這套詞彙讓你能跨平台地推理並移植伺服器設計。反應器對應 epoll(Linux)與 kqueue(BSD/macOS);前攝器對應 IOCP(Windows)與 io_uring(Linux)。跨平台函式庫常呈現前攝器風格的 API(你說「讀這個」、做完時得到回呼),在 Windows 上以真正的前攝器實作,在 Linux 上則於反應器之上模擬——或者,越來越多地,也用 io_uring 在那裡得到真正的前攝器。這些模式講的是「誰執行 I/O」,而不僅是「有沒有用非阻塞通訊端」。

/* 反應器 */ on_readable(fd): n = read(fd, buf, len); process(buf, n); /* 你來讀 */ /* 前攝器 */ start_read(fd, buf, len); ... on_complete(buf, n): process(buf, n); /* 作業系統讀 */

在反應器裡,你的處理常式在就緒時做讀取;在前攝器裡,作業系統做完讀取,再呼叫你的處理常式。

決定性的差別在於「誰搬位元組」:反應器裡,應用程式在就緒信號後自己做 I/O;前攝器裡,核心做 I/O 並發出完成信號。非阻塞的反應器仍是同步 I/O——只有前攝器才是真正的非同步。

又称
dispatcher pattern反應器模式/前攝器模式