Bootstrap

[muduo网络库]——muduo库EventLoopThreadPool类(剖析muduo网络库核心部分、设计思想)

接着之前我们[muduo网络库]——muduo库EventLoopThread类(剖析muduo网络库核心部分、设计思想),我们接下来继续看muduo库中的EventLoopThreadPool类,它和Thread以及EventLoopThread类息息相关。

EventLoopThreadPool类

事件循环线程池,管理所有客户端连接,每个线程都有唯一一个事件循环,可以调用setThreadNum设置线程的数目。线程池中每一个线程都有一个自己的EventLoop,而每一个EventLoop底层都是一个epoll,它利用了自身的epoll在没有事件的时候阻塞住,在有事件发生的时候,epoll监听到了事件就会去处理事件。

重要成员变量

EventLoop *baseLoop_;  //主线程loop
std::string name_;
bool started_; //标记当前状态 即IO线程是否开始运行
int numThreads_; //线程池中线程的数量
int next_;  //负载均衡用
std::vector<std::unique_ptr<EventLoopThread>> threads_;//创建事件的线程
std::vector<EventLoop*> loops_; //事件线程里面EventLoop的指针,每个EventLoopThread线程对应的EventLoop保存在loops_中
  • 具体含义如注释所示

重要成员函数

  • 设置底层线程的数量,状态,名字
void setThreadNum(int numThreads) { numThreads_ = numThreads;}
bool started() const { return started_;}
const std::string name() const { return name_;}
  • 启动线程池,实际上创建numThreads个线程,并让每个eventloopthread调用startLoop()
void EventLoopThreadPool::start(const ThreadInitCallback &cb)
{
    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(std::unique_ptr<EventLoopThread>(t)); //事件循环线程
        loops_.push_back(t->startLoop()); 
    }

    //整个服务端只有一个线程,运行着baseloop
    if (numThreads_ == 0 && cb)
    {
        cb(baseLoop_);//执行
    }
}
  1. 设置当前状态为true,根据需要的线程数numThreads_,创建线程
  2. 在for循环中,先创建一个EventLoopThread对象,构造线程池内线程集合
  3. 调用EventLoopThread::startLoop(),创建线程,绑定一个新的EventLoop,并返回loop地址,放入loops_中,loops_是一个std::vector<EventLoop*>类型,把每个EventLoopThread线程对应的EventLoop保存在loops_中。
  4. 如果没有其他线程,只有主线程的话,直接调用callback
  • 以轮询的方式分配channel给subloop
EventLoop* EventLoopThreadPool::getNextLoop()
{
    EventLoop* loop = baseLoop_;

    if(!loops_.empty())// 通过轮询,获取下一个处理事件的loop
    {
        loop = loops_[next_];
        ++next_;
        if(next_ >= loops_.size())
        {
            next_ = 0;
        }
    }
    return loop;
}

注意: 在TcpServer::newConnection 会通过EventLoop *ioLoop = threadPool_->getNextLoop();初始时loop被赋值为baseLoop_,也就是主线程的loop,如果我们设置子线程为零的话程序也可正常运行,也就是说,getNextLoop的实现意味着muduo是支持单线程的;
如果loops_不为空,loop = loops_[next_],ioLoop就得到了一个subloop

花了很长时间在思考这个cb(baseLoop_)是为什么呢?(个人理解,如果大家觉得有错,麻烦指正)
  1. 在TcpServer::start(),启动IO线程池 threadPool_->start(threadInitCallback_); cb=threadInitCallback_;
  2. 来到了EventLoopThreadPool::start()中,创建一个EventLoopThread对象EventLoopThread *t =new EventLoopThread(cb,buf); ,在这里callback_(cb),先把cb给到了callback_
  3. 接着EventLoopThread::startLoop(),中调用 Thread::start()
  4. 在EventLoopThread的构造函数中,thread_(std::bind(&EventLoopThread::threadFunc,this),name),绑定了threadFunc,在Thread构造函数中func_(std::move(func)),所以会调用EventLoopThread::threadFunc()函数,这里创建了一个EventLoop,并给了callback_;
  5. 所以如果有其他线程,就创建一个loop给回去,没有就会给回去baseLoop_;
  6. EventLoopThreadPool里面的loops_就存了每个EventLoopThread线程对应的EventLoop;
  7. 然后loop_->runInLoop(std::bind(&Acceptor::listen,acceptor_.get()));

代码地址:https://github.com/Cheeron955/mymuduo/tree/master

好了~ 关于muduo库的EventLoopThreadPool类就剖析到这里,与它相关的Thread,EventLoopThread也都完结了,这部分其实有一点不知所云,希望大家发现错误能够批评指正。最后,除去TcpServer,我们就剩下InetAddress类了,下一篇我们来剖析InetAddress类,然后就从整个服务器流程来讲TcpServer ~ 我们下一节见 ~~
;