一. Reactor模式簡介
Reactor釋義“反應堆”,是一種事件驅動機制。和普通函數調用的不同之處在於:應用程式不是主動的調用某個API完成處理,而是恰恰相反,Reactor逆置了事件處理流程,應用程式需要提供相應的介面並註冊到Reactor上,如果相應的時間發生,Reactor將主動調用應用程式註冊的介面,這些介面又稱為“回呼函數”。
二. moduo庫Reactor模式的實現
muduo主要通過3個類來實現Reactor模式:EventLoop,Channel,Poller。
1. EventLoop
事件迴圈。moduo的執行緒模式為one loop per thread,即每個線程只能有一個EventLoop對象。EventLoop對象的生命週期通常和其所屬的線程一樣長。
資料成員:
const pid_t threadId_;儲存當前EventLoop所屬線程id
boost::scoped_ptr poller_; 實現I/O複用 boost::scoped_ptr timerQueue_;
int wakeupFd_;
boost::scoped_ptr wakeupChannel_; 用於處理wakeupFd_上的可讀事件,將事件分發到handlRead() ChannelList activeChannels_; 有事件就緒的 Channel Channel* currentActiveChannel_;
MutexLock mutex_; pendingFunctors_回暴露給其他線程,所以需要加鎖 std::vectorpendingFunctors_;
主要功能函數:
loop(),在該函數中會迴圈執行以下過程:調用Poller::poll(),通過此調用獲得一個vector<channel*>activeChannels_的就緒事件集合,再遍曆該容器,執行每個Channel的Channel::handleEvent()完成相應就緒事件回調,最後執行pendingFunctors_排隊的函數。上述一次迴圈就是一次Reactor模式完成。
runInLoop(boost::function<void()>),實現使用者指定任務回調,若是EventLoop隸屬的線程調用EventLoop::runInLoop()則EventLoop馬上執行;若是其它線程調用則執行EventLoop::queueInLoop(boost::function<void()>將任務添加到隊列中(線程轉移)。EventLoop如何獲得有任務這一事實呢?通過eventfd可以實現線程間通訊,具體做法是:其它線程向EventLoop::vector<boost::function<void()> >新增工作T,然後通過EventLoop::wakeup()向eventfd寫一個int,eventfd的回呼函數EventLoop::handleRead()讀取這個int,從而相當於EventLoop被喚醒,此時loop中遍曆隊列執行堆積的任務。這裡採用Channel管理eventfd,Poller偵聽eventfd體現了eventfd可以統一事件來源的優勢。
更多精彩內容:http://www.bianceng.cnhttp://www.bianceng.cn/Programming/cplus/
queueInLoop(Functor& cb),將cb放入隊列,並在必要時喚醒IO線程。有兩種情況需要喚醒IO線程,1 調用queueInLoop()的線程不是IO線程,2 調用queueInLoop()的線程是IO線程,而此時正在調用pengding functor。
2. Channel
事件分發器。每個Channel只屬於一個EventLoop,每個Channel只負責一個檔案描述符fd的IO事件分發,但其不擁有fd。
資料成員:
int fd_檔案描述符,
int events_ 檔案描述符註冊事件,
int revents_檔案描述符的就緒事件,由Poller::poll設定
readCallback_,writeCallback...各種事件回調,會在擁有該Channel類的建構函式中被註冊,例如TcpConnction會在建構函式中TcpConnection::handlRead()註冊給Channel::readCallback
主要功能函數:
setCallback()系列函數,接受Channel所屬的類註冊相應的事件回呼函數
enableReading(),update(), 當一個fd想要註冊可讀事件時,首先通過Channel::enableReading()-->Channel::update(this)->EventLoop::updateChannel(Channel)->Poller::updateChannel(Channel*)調用鏈向poll系統調用的偵聽事件表註冊或者修改註冊事件。
handleEvent(), Channel作為是事件分發器其核心結構是Channel::handleEvent(),該函數調用Channel::handleEventWithGuard(),在其內根據Channel::revents的值分發調用相應的事件回調。
3. Poller
Poller是IO multiplexing的封裝,封裝了poll和epoll。Poller是EventLoop的間接成員,只供擁有該Poller的EventLoop在IO線程調用。生命期與EventLoop相等。
資料成員:
vector pollfds_事件結構體數組用於poll的第一個參數;
map<int,channel*> channels_用於檔案描述符fd到Channel的映射便於快速尋找到相應的Channel
主要功能函數:
updateChannel(Channel*) 用於將傳入的Channel關心的事件註冊給Poller。
poll(int timeoutMs,vector<channel*> activeChannels)其調用poll偵聽事件集合,將就緒事件所屬的Channel調用fillActiveChannels()加入到activeChannels_中。
其他類
EventLoopThread: 啟動一個線程執行一個EventLoop,其語義和"one loop per thread“相吻合。注意這裡用到了互斥量和條件變數,這是因為線程A建立一個EventLoopThread對象後一個運行EventLoop的線程已經開始建立了,可以通過EventLoopThread::startLoop()擷取這個EventLoop對象,但是若EventLoop線程還沒有建立好,則會出錯。所以在建立EventLoop完成後會執行condititon.notify()通知線程A,線程A調用EventLoopThread::startLoop()時調用condition.wai()等待,從而保證擷取一個建立完成的EventLoop.畢竟線程A建立的EventLoop線程,A可能還會調用EventLoop執行一些任務回調呢。
作者:cnblogs NicGanon</