6.6.2008
Kevin Lynx
Proactor和Reactor都是并發(fā)編程中的設計模式。在我看來,他們都是用于派發(fā)/分離IO操作事件的。這里所謂的
IO事件也就是諸如read/write的IO操作。"派發(fā)/分離"就是將單獨的IO事件通知到上層模塊。兩個模式不同的地方
在于,Proactor用于異步IO,而Reactor用于同步IO。
摘抄一些關鍵的東西:
"
Two patterns that involve event demultiplexors are called Reactor and Proactor [1]. The Reactor patterns
involve synchronous I/O, whereas the Proactor pattern involves asynchronous I/O.
"
關于兩個模式的大致模型,從以下文字基本可以明白:
"
An example will help you understand the difference between Reactor and Proactor. We will focus on the read
operation here, as the write implementation is similar. Here's a read in Reactor:
* An event handler declares interest in I/O events that indicate readiness for read on a particular socket ;
* The event demultiplexor waits for events ;
* An event comes in and wakes-up the demultiplexor, and the demultiplexor calls the appropriate handler;
* The event handler performs the actual read operation, handles the data read, declares renewed interest in
I/O events, and returns control to the dispatcher .
By comparison, here is a read operation in Proactor (true async):
* A handler initiates an asynchronous read operation (note: the OS must support asynchronous I/O). In this
case, the handler does not care about I/O readiness events, but is instead registers interest in receiving
completion events;
* The event demultiplexor waits until the operation is completed ;
* While the event demultiplexor waits, the OS executes the read operation in a parallel kernel thread, puts
data into a user-defined buffer, and notifies the event demultiplexor that the read is complete ;
* The event demultiplexor calls the appropriate handler;
* The event handler handles the data from user defined buffer, starts a new asynchronous operation, and returns
control to the event demultiplexor.
"
可以看出,兩個模式的相同點,都是對某個IO事件的事件通知(即告訴某個模塊,這個IO操作可以進行或已經(jīng)完成)。在結構
上,兩者也有相同點:demultiplexor負責提交IO操作(異步)、查詢設備是否可操作(同步),然后當條件滿足時,就回調(diào)handler。
不同點在于,異步情況下(Proactor),當回調(diào)handler時,表示IO操作已經(jīng)完成;同步情況下(Reactor),回調(diào)handler時,表示
IO設備可以進行某個操作(can read or can write),handler這個時候開始提交操作。
用select模型寫個簡單的reactor,大致為:

/**////
class handler


{
public:
virtual void onRead() = 0;
virtual void onWrite() = 0;
virtual void onAccept() = 0;
};

class dispatch


{
public:
void poll()

{
// add fd in the set.
//
// poll every fd
int c = select( 0, &read_fd, &write_fd, 0, 0 );
if( c > 0 )

{
for each fd in the read_fd_set

{ if fd can read
_handler->onRead();
if fd can accept
_handler->onAccept();
}

for each fd in the write_fd_set

{
if fd can write
_handler->onWrite();
}
}
}

void setHandler( handler *_h )

{
_handler = _h;
}

private:
handler *_handler;
};


/**//// application
class MyHandler : public handler


{
public:
void onRead()

{
}

void onWrite()

{
}

void onAccept()

{
}
};


在網(wǎng)上找了份Proactor模式比較正式的文檔,其給出了一個總體的UML類圖,比較全面:
根據(jù)這份圖我隨便寫了個例子代碼:
class AsyIOProcessor


{
public:
void do_read()

{
//
send read operation to OS
// read io finished.and dispatch notification
_proactor->dispatch_read();
}

private:
Proactor *_proactor;
};

class Proactor


{
public:
void dispatch_read()

{
_handlerMgr->onRead();
}

private:
HandlerManager *_handlerMgr;
};

class HandlerManager


{
public:
typedef std::list<Handler*> HandlerList;

public:
void onRead()

{
// notify all the handlers.
std::for_each( _handlers.begin(), _handlers.end(), onRead );
}

private:
HandlerList *_handlers;
};

class Handler


{
public:
virtual void onRead() = 0;
};

// application level handler.
class MyHandler : public Handler


{
public:
void onRead()

{
//
}
};


Reactor通過某種變形,可以將其改裝為Proactor,在某些不支持異步IO的系統(tǒng)上,也可以隱藏底層的實現(xiàn),利于編寫跨平臺
代碼。我們只需要在dispatch(也就是demultiplexor)中封裝同步IO操作的代碼,在上層,用戶提交自己的緩沖區(qū)到這一層,
這一層檢查到設備可操作時,不像原來立即回調(diào)handler,而是開始IO操作,然后將操作結果放到用戶緩沖區(qū)(讀),然后再
回調(diào)handler。這樣,對于上層handler而言,就像是proactor一樣。詳細技法參見這篇文章。
其實就設計模式而言,我個人覺得某個模式其實是沒有完全固定的結構的。不能說某個模式里就肯定會有某個類,類之間的
關系就肯定是這樣。在實際寫程序過程中也很少去特別地實現(xiàn)某個模式,只能說模式會給你更多更好的架構方案。
最近在看spserver的代碼,看到別人提各種并發(fā)系統(tǒng)中的模式,有點眼紅,于是才來掃掃盲。知道什么是leader follower模式,
reactor, proactor,multiplexing,對于心中的那個網(wǎng)絡庫也越來越清晰。
最近還干了些離譜的事,寫了傳說中的字節(jié)流編碼,用模板的方式實現(xiàn),不但保持了擴展性,還少寫很多代碼;處于效率考慮,
寫了個static array容器(其實就是template <typename _Tp, std::size_t size> class static_array { _Tp _con[size]),
加了iterator,遵循STL標準,可以結合進STL的各個generic algorithm用,自我感覺不錯。基礎模塊搭建完畢,解析了公司
服務器網(wǎng)絡模塊的消息,我是不是真的打算用自己的網(wǎng)絡模塊重寫我的驗證服務器?在另一個給公司寫的工具里,因為實在厭惡
越來越多的重復代碼,索性寫了幾個宏,還真的做到了代碼的自動生成:D。
對優(yōu)雅代碼的追求真的成了種癖好. = =|