muduo庫源碼剖析(一) reactor模式

來源:互聯網
上載者:User

一. 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</

相關文章

E-Commerce Solutions

Leverage the same tools powering the Alibaba Ecosystem

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。