muduo源码分析之EventLoopThreadPool

相关文件

//IO线程池
muduo/net/EventLoopThreadPool.h
muduo/net/EventLoopThreadPool.cc

//IO线程类
muduo/net/EventLoopThread.h
muduo/net/EventLoopTHread.cc

作用

EventLoopThread IO线程类会启动自己的线程,并在其中运行EventLoop::loop()。

EventLoopThreadPool IO线程池是TCPserver的一个成员,功能式开启若干个IO线程,并让这些IO线程处于事件循环的状态。

在muduo中,使用one loop per thread的思想实现多线程TcpServer。关键步骤是在建立新连接TcpConnection时,从IO线程池中挑选一个loop构造TcpConnection。
多线程TcpServer自己的EventLoop只用来接受新连接,而
新连接会用其他EventLoop来执行IO。

image
图中,每个Reacctor都属于一个线程,mainReactor关注的是acceptor,即监听套接字listenfd相关的事件。subReactor关注的是已连接套接字connfd相关的事件。

使用

TcpServer类中包含一个

std::shared_ptr<EventLoopThreadPool> threadPool_;

数据成员,在构造函数中初始化。

建立新连接时,调用threadPool_->getNextLoop()从IO线程池取一个EventLoop构造TcpConnection。

//TcpServer.cc
void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)
{
  loop_->assertInLoopThread();
  EventLoop* ioLoop = threadPool_->getNextLoop();//!!取一个loop
  char buf[64];
  snprintf(buf, sizeof buf, "-%s#%d", ipPort_.c_str(), nextConnId_);
  ++nextConnId_;
  string connName = name_ + buf;

  LOG_INFO << "TcpServer::newConnection [" << name_
           << "] - new connection [" << connName
           << "] from " << peerAddr.toIpPort();
  InetAddress localAddr(sockets::getLocalAddr(sockfd));
  // FIXME poll with zero timeout to double confirm the new connection
  // FIXME use make_shared if necessary
  //!!用ioloop构造新连接
  TcpConnectionPtr conn(new TcpConnection(ioLoop,
                                          connName,
                                          sockfd,
                                          localAddr,
                                          peerAddr));
  connections_[connName] = conn;
  conn->setConnectionCallback(connectionCallback_);
  conn->setMessageCallback(messageCallback_);
  conn->setWriteCompleteCallback(writeCompleteCallback_);
  conn->setCloseCallback(
      std::bind(&TcpServer::removeConnection, this, _1)); // FIXME: unsafe
  //!!在ioloop中执行TcpConnection的回调函数
  ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));
}

EventLoopThread源码分析

EventLoopThread类

对外接口函数只有startLoop(),用于开启事件循环。
在构造EventLoopThread类时,注册线程函数threadFunc()。

//类声明
class EventLoopThread : noncopyable
{
 public:
  typedef std::function<void(EventLoop*)> ThreadInitCallback;

  EventLoopThread(const ThreadInitCallback& cb = ThreadInitCallback(),
                  const string& name = string());
  ~EventLoopThread();
  EventLoop* startLoop();

 private:
  void threadFunc();

  EventLoop* loop_ GUARDED_BY(mutex_);
  bool exiting_;
  Thread thread_;
  MutexLock mutex_;
  Condition cond_ GUARDED_BY(mutex_);
  ThreadInitCallback callback_;
};

开启事件循环,其实是在线程函数中执行EventLoop::loop(),
startLoop()返回EventLoop指针。

EventLoop* EventLoopThread::startLoop()
{
  assert(!thread_.started());
  thread_.start(); //执行线程函数

  EventLoop* loop = NULL;
  {
    MutexLockGuard lock(mutex_);
    while (loop_ == NULL)
    {
      cond_.wait(); //等待loop_
    }
    loop = loop_;
  }

  return loop;
}

//线程函数
void EventLoopThread::threadFunc()
{
  EventLoop loop;

  if (callback_)
  {
    callback_(&loop);//执行线程初始化回调函数,在构造类时注册
  }

  {
    MutexLockGuard lock(mutex_);
    loop_ = &loop;
    cond_.notify(); //通知startLoop可返回EventLoop指针
  }

  loop.loop();  //开启事件循环
  //assert(exiting_);
  MutexLockGuard lock(mutex_);
  loop_ = NULL;
}

EventLoopThreadPool源码分析

EventLoopThreadPool类

class EventLoopThreadPool : noncopyable
{
 public:
  typedef std::function<void(EventLoop*)> ThreadInitCallback;

  EventLoopThreadPool(EventLoop* baseLoop, const string& nameArg);  //构造函数
  ~EventLoopThreadPool(); //析构函数
  void setThreadNum(int numThreads) { numThreads_ = numThreads; } //设置线程数
  void start(const ThreadInitCallback& cb = ThreadInitCallback()); //创建IO线程,开启事件循环

  // valid after calling start()
  /// round-robin
  EventLoop* getNextLoop();//返回一个EventLoop指针

  /// with the same hash code, it will always return the same EventLoop
  EventLoop* getLoopForHash(size_t hashCode);

  std::vector<EventLoop*> getAllLoops();

  bool started() const
  { return started_; }

  const string& name() const
  { return name_; }

 private:

  EventLoop* baseLoop_; //TcpServer所在的EventLoop
  string name_;
  bool started_;
  int numThreads_; //线程数
  int next_;//新连接到来所选择的Eventloop对象的下标
  std::vector<std::unique_ptr<EventLoopThread>> threads_;  //存放EventLoopThread的vector
  std::vector<EventLoop*> loops_; //存放EventLoop的vector
};

创建IO线程

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(std::unique_ptr<EventLoopThread>(t));
    loops_.push_back(t->startLoop());
  }
  if (numThreads_ == 0 && cb)
  {
    cb(baseLoop_);
  }
}

取一个IO线程

这里采用最简单的round-robin算法来选取pool中的EventLoop。
即按数组下标依次取。

EventLoop* EventLoopThreadPool::getNextLoop()
{
  baseLoop_->assertInLoopThread();
  assert(started_);
  EventLoop* loop = baseLoop_;

  if (!loops_.empty())
  {
    // round-robin
    loop = loops_[next_];
    ++next_;
    if (implicit_cast<size_t>(next_) >= loops_.size())
    {
      next_ = 0;
    }
  }
  return loop;
}
posted @ 2021-05-28 16:42  零十  阅读(143)  评论(0编辑  收藏  举报