The concurrency model for Muduo is one loop per thread+ ThreadPool. For ease of use, the Muduo encapsulates the EventLoop and thread as Eventloopthread, in order to facilitate the use of the thread pool and eventloopthread encapsulation as eventloopthreadpool. So this blog post does not involve fresh technology, but there are some aspects of encapsulation and logic that we need to analyze and understand.
Eventloopthread
any thread, as long as EventLoop is created and run, is an IO thread. the Eventloopthread class is a well-encapsulated IO thread.
the Eventloopthread workflow is:
1. Create the Eventloopthread object in the main thread.
2. The main thread calls Eventloopthread.start (), starts the thread in the Eventloopthread (called the IO thread), and the main thread waits for the IO thread to create the EventLoop object.
3. The IO thread calls ThreadFunc to create the EventLoop object. Notifies the main thread that the master thread has been created.
4. The main thread returns the created EventLoop object.
EventLoopThread.h
Class eventloopthread:boost::noncopyable{public: typedef boost::function<void (eventloop*) > Threadinitcallback; Eventloopthread (const threadinitcallback& CB = Threadinitcallback ()); ~eventloopthread (); eventloop* Startloop ();//start thread, the thread becomes the IO thread private: void ThreadFunc ();//thread function eventloop* loop_;//loop_ The pointer points to a EventLoop object , bool Exiting_; Thread Thread_; Mutexlock mutex_; Condition Cond_; Threadinitcallback callback_;//callback function is called before the Eventloop::loop event loop};
eventloopthread.cc
Eventloopthread::eventloopthread (const threadinitcallback& CB): Loop_ (NULL), Exiting_ (false), Thread_ (boost::b IND (&eventloopthread::threadfunc, this)), Mutex_ (), Cond_ (mutex_), Callback_ (CB) {}eventloopthread::~eventloop Thread () {exiting_ = true; Loop_->quit ();//exit the IO thread and let the IO thread loop exit, thus exiting the IO thread thread_.join (); Wait for the thread to exit}eventloop* Eventloopthread::startloop () {assert (!thread_.started ()); Thread_.start ();//thread Start, call ThreadFunc () {Mutexlockguard lock (mutex_); while (loop_ = = NULL) {cond_.wait ();//need to wait for EventLoop object to be created}} return loop_;} void Eventloopthread::threadfunc () {EventLoop loop; if (callback_) {callback_ (&loop); } {Mutexlockguard lock (mutex_); The Loop_ pointer points to an object on the stack, and after the ThreadFunc function exits, the pointer fails//the ThreadFunc function exits, which means that the thread exits, and the Eventloopthread object has no value. So there's no big problem loop_ = &loop; Cond_.notify (); Create good, send notification} loop.loop ();//will loop here until Eventloopthread is refactored. Thereafter no longer use LOOP_ to access EventLoop//assert (exiting_);}Test procedure:
#include <muduo/net/EventLoop.h> #include <muduo/net/EventLoopThread.h> #include <stdio.h>using Namespace Muduo;using namespace Muduo::net;void runinthread () { printf ("Runinthread (): PID =%d, Tid =%d\n", Getpid (), Currentthread::tid ());} int main () { printf ("Main (): PID =%d, Tid =%d\n", getpid (), Currentthread::tid ()); Eventloopthread Loopthread; eventloop* loop = Loopthread.startloop (); Call Runinthread asynchronously, add runinthread to the IO thread where the loop object is located, and let the IO thread execute loop->runinloop (runinthread); Sleep (1); RunAfter internally also calls the Runinloop, so here is also the asynchronous call Loop->runafter (2, runinthread); Sleep (3); Loop->quit (); printf ("Exit main () \ n");}
Analyze the calling process: (view log)
Main thread CallLoop->runinloop (runinthread);because the main thread (notIOthread) CallRuninloop, so callQueueinloop ()will beRunintheadadd to the queue, and thenWakeup () IOthreads,IOThread indopendingfunctors ()Medium TakeLoop->runafter ()to wake up, this is just the executionRunAfter ()has added a2sthe timer, 2sTimeout,Timerfd_readable, firstHandleread ()and then execute the callback functionRuninthread ().
then why exit Main () after wakeupfd_ eventloopthead loop_->quit (), because it is not in Span style= "Font-family:calibri" >io thread call quit () io thread can be from poll return, so that again cycle judgment while (!quit_) can exit io thread.
EventloopthreadpoolMuduo Threading Model:
Muduo thought when Eventloop+thread pool, in order to be more convenient to use, will eventloopthread do the encapsulation. Main reactor can create sub reactor, and some tasks are distributed to sub reactor. The idea of Eventloopthreadpool is relatively simple, creating eventloopthreadpool with a main reactor. Binding EventLoop and thread in Eventloopthreadpool, you can return EventLoop objects to use the thread in Eventloopthreadpool.
EventLoopThreadPool.h
Class eventloopthreadpool:boost::noncopyable{public: typedef boost::function<void (eventloop*) > Threadinitcallback; Eventloopthreadpool (eventloop* baseloop); ~eventloopthreadpool (); void Setthreadnum (int numthreads) {Numthreads_ = numthreads;} void Start (const threadinitcallback& CB = Threadinitcallback ()); eventloop* Getnextloop (); Private: eventloop* baseloop_;//and acceptor belong to eventloop the same bool started_; int numthreads_;//thread number int next_;//New connection arrives, the selected EventLoop object subscript boost::p tr_vector<eventloopthread> threads_;//IO thread list std::vector<eventloop*> loops_;//eventloop list};
eventloopthreadpool.cc
Eventloopthreadpool::eventloopthreadpool (eventloop* baseloop): Baseloop_ (Baseloop), Started_ (false), NumThreads_ (0 ), Next_ (0) {}eventloopthreadpool::~eventloopthreadpool () {//Don ' t delete loop, it's stack variable}void Eventloopthre Adpool::start (const threadinitcallback& CB) {assert (!started_); Baseloop_->assertinloopthread (); Started_ = true; for (int i = 0; i < Numthreads_; ++i) {eventloopthread* t = new Eventloopthread (CB); Threads_.push_back (t); Loops_.push_back (T->startloop ());//start Eventloopthread thread, call CB} if (Numthreads_ = = 0 && cb) before entering the event loop {/ /Only one eventloop, before this eventloop into the event loop, call CB CB (BASELOOP_); }}eventloop* Eventloopthreadpool::getnextloop () {baseloop_->assertinloopthread (); eventloop* loop = Baseloop_; If Loops_ is empty, loop points to baseloop_//If it is not empty, select a EventLoop if (!loops_.empty ()) {//Round-robin according to Round-robin (RR, round-robin) dispatch mode loop = Loops_[next_]; ++next_; if (implicit_cast<size_t> (next_) >= lOops_.size ()) {next_ = 0; }} return loop;}
Mainreactor concerns the listener event, the connected socket event polls the subreactors processing in the thread pool, and a new connection corresponds to a subreactor
We use round-robin(RR, round call) scheduling mode to Select a EventLoop The Getnextloop function. in extreme cases, the number of threads in the pool is 0 o'clock , then the new connection is given to mainreactor, which degrades into a single-threaded pattern.
Muduo Network Library Source code Analysis (iv) Encapsulation of Eventloopthread and Eventloopthreadpool