muduo_net程式碼剖析之EventLoop
阿新 • • 發佈:2018-11-26
TCP網路程式設計本質
TCP網路程式設計最本質的是處理三個半事件
- 連線建立:伺服器accept(被動)接收連線,客戶端connect(主動)發起連線
- 連線斷開:主動斷開(close、shutdown),被動斷開(read返回0)
- 訊息到達:檔案描述符可讀
- 訊息傳送完畢:這算半個。對於低流量的服務,可不必關心這個事件;這裡的傳送完畢是指資料寫入作業系統緩衝區,將由TCP協議棧負責資料的傳送與重傳,不代表對方已經接收到資料
EventLoop是整個Reactor的核心。其類圖如下:
one loop per thread意味著每個執行緒只能有一個EventLoop物件,用變數
__thread EventLoop* t_loopInThisThread = 0;
表示,在建立EventLoop物件時將t_loopInThisThread賦值,以後再建立時就可以檢查這個變數,如果已經賦值就說明當前執行緒已經建立過EventLoop物件了。執行緒呼叫靜態函式EventLoop::getEventLoopOfCurrentThread就可以獲得當前執行緒的EventLoop物件的指標了。
對EventLoop的個人理解
- 與libevent的event_base類似,EventLoop也是一個事件迴圈,即EventLoop本質上是一個while迴圈,在while迴圈中呼叫了epoll_wait/poll監聽註冊在EventLoop上的事件/Channel
- EventLoop的使用與libevent的event_base一樣,就是呼叫loop()函式對安插在EventLoop物件上的事件進行監聽
EventLoop::loop()的時序圖
- loop.loop()實際上是在while迴圈中呼叫了poll或epoll的poll函式,程式在此處阻塞,直到有事件被觸發
- 當監聽到事件被觸發後,呼叫handleEvent()函式去處理事件
//事件迴圈,該函式不能跨執行緒呼叫,只能在建立該物件的執行緒中呼叫
void EventLoop::loop()
{
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();
//呼叫poll()[ 本質上epoll的epoll_wait、poll的poll() ],阻塞在這裡
//poll返回後,activeChannels_活動通道被填充
pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);
++iteration_;
if (Logger::logLevel() <= Logger::TRACE)
{
printActiveChannels();
}
eventHandling_ = true;
//遍歷每一個活動通道,並處理事件handleEvent
for (Channel* channel : activeChannels_)
{
currentActiveChannel_ = channel;
currentActiveChannel_->handleEvent(pollReturnTime_);
}
currentActiveChannel_ = NULL;
eventHandling_ = false;
doPendingFunctors(); //執行pendingFunctors_中的任務
}
LOG_TRACE << "EventLoop " << this << " stop looping";
looping_ = false;
}
示例程式碼
父子執行緒都建立了自己的loop,並且監控安插在各自身上的事件
#include <muduo/net/EventLoop.h>
#include <muduo/base/Thread.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
using namespace muduo;
using namespace muduo::net;
void callback()
{
printf("callback(): pid = %d, tid = %d\n", getpid(), CurrentThread::tid());
EventLoop anotherLoop;
}
void threadFunc()
{
printf("threadFunc(): pid = %d, tid = %d\n", getpid(), CurrentThread::tid());
assert(EventLoop::getEventLoopOfCurrentThread() == NULL);
EventLoop loop; //子執行緒中的loop
assert(EventLoop::getEventLoopOfCurrentThread() == &loop);
loop.runAfter(1.0, callback); //1s後,呼叫callback
loop.loop();
}
int main()
{
printf("main(): pid = %d, tid = %d\n", getpid(), CurrentThread::tid());
assert(EventLoop::getEventLoopOfCurrentThread() == NULL);
EventLoop loop; //主執行緒中的loop
assert(EventLoop::getEventLoopOfCurrentThread() == &loop);
Thread thread(threadFunc);
thread.start(); //啟動子執行緒
loop.loop(); //主執行緒中的loop呼叫loop()
}