EventLoopThread EventLoopThreadPool
muduo的並行存取模型為one loop per thread+ threadpool。為了方便使用,muduo封裝了EventLoop和Thread為EventLoopThread,為了方便使用線程池,又把EventLoopThread封裝為EventLoopThreadPool。 EventLoopThread
任何一個線程,只要建立並運行了EventLoop,就是一個IO線程。
EventLoopThread類就是一個封裝了的IO線程。
EventLoopThread的工作流程為:
1、在主線程(暫且這麼稱呼)建立EventLoopThread對象。
2、主線程調用EventLoopThread.start(),啟動EventLoopThread中的線程(稱為IO線程),這是主線程要等待IO線程建立完成EventLoop對象。
3、IO線程調用threadFunc建立EventLoop對象。通知主線程已經建立完成。
4、主線程返回建立的EventLoop對象。
EventLoopThread.h
class EventLoopThread : boost::noncopyable{ public: typedef boost::function<void(EventLoop*)> ThreadInitCallback; EventLoopThread(const ThreadInitCallback& cb = ThreadInitCallback(), const string& name = string()); ~EventLoopThread(); EventLoop* startLoop();//啟動本線程,返回本線程中的EventLoop private: void threadFunc(); EventLoop* loop_;//本線程持有的EventLoop對象指標 bool exiting_;//是否已經退出 Thread thread_;//本線程 MutexLock mutex_; Condition cond_; ThreadInitCallback callback_;//回呼函數};
EventLoopThread.cc
EventLoopThread::EventLoopThread(const ThreadInitCallback& cb, const string& name) : loop_(NULL), exiting_(false), thread_(boost::bind(&EventLoopThread::threadFunc, this), name),//建立線程,在回呼函數建立EventLoop mutex_(), cond_(mutex_), callback_(cb){}EventLoopThread::~EventLoopThread(){ exiting_ = true; if (loop_ != NULL) // not 100% race-free, eg. threadFunc could be running callback_. { // still a tiny chance to call destructed object, if threadFunc exits just now. // but when EventLoopThread destructs, usually programming is exiting anyway. loop_->quit();//退出loop迴圈 thread_.join();//等待線程退出 }}EventLoop* EventLoopThread::startLoop()//另一個線程在調用這個函數{ assert(!thread_.started()); thread_.start();//當前線程啟動,調用threadFunc() { MutexLockGuard lock(mutex_); while (loop_ == NULL) { cond_.wait();//等待建立好當前IO線程 } } return loop_;}void EventLoopThread::threadFunc(){ EventLoop loop;//建立EventLoop對象。注意,在棧上 if (callback_) { callback_(&loop); } { MutexLockGuard lock(mutex_); loop_ = &loop; cond_.notify();//通知startLoop } loop.loop();//會在這裡迴圈,直到EventLoopThread析構。此後不再使用loop_訪問EventLoop了 //assert(exiting_); loop_ = NULL;}
EventLoopThreadPool
muduo的思想時eventLoop+thread pool,為了更方便使用,將EventLoopThread做了封裝。main reactor可以建立sub reactor,並發一些任務分發到sub reactor中去。
EventLoopThreadPool的思想比較簡單,用一個main reactor建立EventLoopThreadPool。在EventLoopThreadPool中將EventLoop和Thread綁定,可以返回EventLoop對象來使用EventLoopThreadPool中的Thread。
EventLoopThreadPool.h
class EventLoop;class EventLoopThread : boost::noncopyable{ public: typedef boost::function<void(EventLoop*)> ThreadInitCallback; EventLoopThread(const ThreadInitCallback& cb = ThreadInitCallback(), const string& name = string()); ~EventLoopThread(); EventLoop* startLoop();//啟動本線程,返回本線程中的EventLoop private: void threadFunc(); EventLoop* loop_;//本線程持有的EventLoop對象指標 bool exiting_;//是否已經退出 Thread thread_;//本線程 MutexLock mutex_; Condition cond_; ThreadInitCallback callback_;//回呼函數};
EventLoopThreadPool.cc
EventLoopThreadPool::EventLoopThreadPool(EventLoop* baseLoop, const string& nameArg) : baseLoop_(baseLoop), name_(nameArg), started_(false), numThreads_(0), next_(0){}EventLoopThreadPool::~EventLoopThreadPool(){ // Don't delete loop, it's stack variable}void EventLoopThreadPool::start(const ThreadInitCallback& cb){ assert(!started_); baseLoop_->assertInLoopThread(); started_ = true; for (int i = 0; i < numThreads_; ++i) { char buf[name_.size() + 32]; snprintf(buf, sizeof buf, "%s%d", name_.c_str(), i); EventLoopThread* t = new EventLoopThread(cb, buf); threads_.push_back(t); loops_.push_back(t->startLoop()); } if (numThreads_ == 0 && cb) { cb(baseLoop_); }}EventLoop* EventLoopThreadPool::getNextLoop(){ baseLoop_->assertInLoopThread(); assert(started_); EventLoop* loop = baseLoop_;//loops_為空白,則返回baseloop if (!loops_.empty())//迴圈分配 { // round-robin loop = loops_[next_]; ++next_; if (implicit_cast<size_t>(next_) >= loops_.size()) { next_ = 0; } } return loop;}EventLoop* EventLoopThreadPool::getLoopForHash(size_t hashCode){ baseLoop_->assertInLoopThread(); EventLoop* loop = baseLoop_; if (!loops_.empty()) { loop = loops_[hashCode % loops_.size()];//根據hashCode分配 } return loop;}std::vector<EventLoop*> EventLoopThreadPool::getAllLoops(){ baseLoop_->assertInLoopThread(); assert(started_); if (loops_.empty()) { return std::vector<EventLoop*>(1, baseLoop_); } else { return loops_; }}
可以寫個簡單的測試程式,建立一個EventLoopThreadPool,列印其中線程的ID和name。
#include <muduo/net/EventLoop.h>#include <muduo/net/EventLoopThread.h>#include <muduo/net/EventLoopThreadPool.h>#include <stdio.h>using namespace muduo;using namespace muduo::net;void runInThread(){ printf("runInThread(): name = %s, tid = %d\n", CurrentThread::name(), CurrentThread::tid());}int main(){ printf("main(): pid = %d, tid = %d\n", getpid(), CurrentThread::tid()); runInThread(); EventLoop loop; EventLoopThreadPool loopThreadPool(&loop, "sub Reactor"); loopThreadPool.setThreadNum(5); loopThreadPool.start(); for(int i=0; i<10; ++i) { EventLoop* loopFromPool=loopThreadPool.getNextLoop(); loopFromPool->runInLoop(runInThread); } sleep(3); printf("exit main().\n");}