標籤:muduo eventloop reactor
EventLoop是整個Reactor的核心。其類圖如下:
one loop per thread意味著每個線程只能有一個EventLoop對象,用變數
__thread EventLoop* t_loopInThisThread = 0;
表示,在建立EventLoop對象時將t_loopInThisThread賦值,以後再建立時就可以檢查這個變數,如果已經賦值就說明當前線程已經建立過EventLoop對象了。線程調用靜態函數EventLoop::getEventLoopOfCurrentThread就可以獲得當前線程的EventLoop對象的指標了。
EventLoop有許多變數,幾個bool變數,looping_:是否正在執行loop迴圈;quit_:是否已經調用quit()函數退出loop迴圈;eventHandling是否正在處理event事件;callingPendingFunctors是否正在調用pendingFunctors_的函數對象。
其他變數,poller_是用來調用pool或epool的,activeChannels_記錄這啟用事件的集合,currentActiveChannel_是當前正在處理的channel事件,pendingFunctors_是當前線程要執行任務的集合。可以在loop()函數中看到這一點:
void EventLoop::loop()//EventLoop在這裡迴圈{ assert(!looping_); assertInLoopThread(); looping_ = true; quit_ = false; // FIXME: what if someone calls quit() before loop() ? LOG_TRACE << "EventLoop " << this << " start looping"; while (!quit_) { activeChannels_.clear();//清空啟用事件集合 pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);//pool_wait或epoll_wait。阻塞在這裡 ++iteration_; if (Logger::logLevel() <= Logger::TRACE) { printActiveChannels(); } // TODO sort channel by priority eventHandling_ = true; for (ChannelList::iterator it = activeChannels_.begin(); it != activeChannels_.end(); ++it) { currentActiveChannel_ = *it; currentActiveChannel_->handleEvent(pollReturnTime_);//事件處理I/O } currentActiveChannel_ = NULL; eventHandling_ = false; doPendingFunctors(); } LOG_TRACE << "EventLoop " << this << " stop looping"; looping_ = false;}
線程在poller_->poll等待監聽事件的到來,當poller_->poll返回後,監聽到的事件放到了activeChannel中,隨後一一處理啟用事件。
最後面調用doPendingFunctors()是執行pendingFunctors_中的任務。
void EventLoop::doPendingFunctors()//執行任務隊列中的任務{ std::vector<Functor> functors; callingPendingFunctors_ = true; { MutexLockGuard lock(mutex_);//盡量讓臨界區小 functors.swap(pendingFunctors_); } for (size_t i = 0; i < functors.size(); ++i) { functors[i](); } callingPendingFunctors_ = false;}
EventLoop的owner線程除了等待poll、執行poll返回的啟用事件,還可以處理一些其他任務,例如調用某一個回呼函數,處理其他EventLoop對象的,調用void EventLoop::runInLoop(const Functor& cb)即可讓EventLoop的owner線程執行cb函數。
void EventLoop::runInLoop(const Functor& cb)//可以誇線程調用{ if (isInLoopThread())//當前線程是ower線程則立即執行,否則放到ower線程的任務隊列,非同步執行 { cb(); } else { queueInLoop(cb); }}void EventLoop::queueInLoop(const Functor& cb){ { MutexLockGuard lock(mutex_); pendingFunctors_.push_back(cb); }//不是EventLoop的owner線程,或者是當前線程,但是正在執行任務隊列中的任務 if (!isInLoopThread() || callingPendingFunctors_) { wakeup();//喚醒owner線程 }}
如果時EventLoop的owner線程,會調用runInLoop會立即執行回呼函數cb,否則會把回呼函數放到任務隊列(其即時vector),即調用queueInLoop函數。如果不是當前線程調用,或者正在執行pendingFunctors_中的任務,都要喚醒EventLoop的owner線程,讓其執行pendingFunctors_中的任務。如果正在執行pendingFunctors_中的任務,添加新任務後不會執行新的任務,因為functors.swap(pendingFunctors_)後,執行的時functors中的任務。
這裡的喚醒wakeup()用了eventfd。這是2.6核心新增的一個技術。
#include <sys/eventfd.h> int eventfd(unsigned int initval, int flags);
建立一個eventfd的fd後,就可以對它進行read、write等操作。eventfd相當於一個計數器,read以後計數器清零,write遞增計數器;fd可以進行如下操作:select(poll、epoll)、close操作。
EventLoop中的wakeupFd_就是eventfd,wakeupChannel_和wakeupFd_相關聯,EventLoop關注了wakeupChannel_的讀事件,當要喚醒(即poller_->poll)時,寫wakeupFd_即可。
void EventLoop::wakeup()//wakeupFd寫,喚醒讀wakeupFd的線程{ uint64_t one = 1; ssize_t n = sockets::write(wakeupFd_, &one, sizeof one); if (n != sizeof one) { LOG_ERROR << "EventLoop::wakeup() writes " << n << " bytes instead of 8"; }}
EventLoop中還有定時器,可以在某一時刻(runAt),未來多久(runAfter),每隔多久(runEvery)執行某一函數。定時用到了TimerQueue,稍後分析它。
TimerId EventLoop::runAt(const Timestamp& time, TimerCallback&& cb){ return timerQueue_->addTimer(std::move(cb), time, 0.0);}TimerId EventLoop::runAfter(double delay, TimerCallback&& cb){ Timestamp time(addTime(Timestamp::now(), delay)); return runAt(time, std::move(cb));}TimerId EventLoop::runEvery(double interval, TimerCallback&& cb){ Timestamp time(addTime(Timestamp::now(), interval)); return timerQueue_->addTimer(std::move(cb), time, interval);}
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
muduo::EventLoop分析