muduo库的线程池ThreadPool剖析

本文深入剖析了muduo库的线程池ThreadPool,它是TcpServer的成员,用于分配TcpConnection到特定的EventLoop线程。muduo网络库采用one loop per thread+ threadpool模型,EventLoopThreadPool的numThreads_参数决定了并发模式,通过getNextLoop()接口按轮询方式选择EventLoop。TcpConnection的管理与移除操作保证在对应的线程中执行,实现了多线程并发处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文地址: https://blog.youkuaiyun.com/Mr_scott_j/article/details/111172724

EventLoopThreadPool也是派生自noncopyable,
用于创建IO线程池,用于把TcpConnection分派到某个具体的EventLoop线程上。EventLoopThreadPool是TcpServer的成员,生命周期由后者控制。

EventLoop应该比较熟悉,就是整个muduo库的核心,就是Reactor模式当中的Dispatcher(事件分发器),具体至EventLoop章节进行详细分析。

EventLoopThread启动了一个线程,并在其中运行EventLoop::loop(),实际上就是对实I/O线程的封装,亦不在此节详细说明。

muduo网络库的作者陈硕在书中提到,muduo的网络库的并发模型为one loop per thread+ threadpool模型。 每个线程最多有一个EventLoop,每个TcpConnection必须归某个EventLoop管理,所有的IO会转移到这个线程。TcpConnection所属的线程由其所属的EventLoop来决定。

为什么先选择EventLoopThreadPool类来进行剖析呢,因为其实这里就是多Reactor/多线程模式的实现(注:单Reactor时只需将numThreads_设置为0)。

在这里插入图片描述EventLoopThreadPool接口如下:
其实只需关注start()、getNextLoop();

class EventLoopThreadPool : noncopyable
{
 public:
  typedef std::function<void(EventLoop*)> ThreadInitCallback;
  EventLoopThreadPool(EventLoop* baseLoop, const string& nameArg);
  ~EventLoopThreadPool();
  void setThreadNum(int numThreads) { numThreads_ = numThreads; }//设置reactor数目
  void start(const ThreadInitCallback& cb = ThreadInitCallback());//启动线程池
  // valid after calling start()
  /// round-robin
  EventLoop* getNextLoop();//根据round-robin选择pool中的EventLoop
  /// with the same hash code, it will always return the same EventLoop
  EventLoop* getLoopForHash(size_t hashCode);
  std::vector<EventLoop*> getAllLoops();
  bool started() constconst string& name() constprivate:
  EventLoop* baseLoop_;//主线程的EventLoop
  string name_;
  bool started_;
  int numThreads_;//线程数量  默认值为0
  int next_;
  std::vector<std::unique_ptr<EventLoopThread>> threads_;
  std::vector<EventLoop*> loops_;
};

numThreads_决定了我们到底选择一种什么样的模式运行:numThreads_==1时,具有一个subReactor;numThreads_==0时,没有subReactor;numThreads_>1时,根据round-robin在线程池中选择一个Reactor。

void EventLoopThreadPool::start(const ThreadInitCallback& cb)
{
  ......
  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));
    //启动每个EventLoopThread线程进入loop(),并且把返回的每个EventLoop指针压入到loops_
    loops_.push_back(t->startLoop());
  }
  if (numThreads_ == 0 && cb)
  {
    //只有一个EventLoop,在这个EventLoop进入事件循环之前,调用cb
    cb(baseLoop_);
  }
}

getNextLoop()接口是给TcpServer使用的,每次有新TcpConnection建立,就会选择一个EventLoop;如果是单线程,则返回baseLoop_(TcpServer自己用的loop)。

至此,我们可以回顾之前在TcpConnection剖析中提到的将TcpServer::removeConnection()进行了一种拆分,为的就是保证removeConnection()操作移交回TcpServer自己的loop线程去,之后再将TcpConnection::connectDestroyed()移动回TcpConnection自己的ioLoop线程进行。也就是说,成员函数属于哪个对象,我们就让哪个函数来执行它。

可见,TcpConnection和TcpServer都是只处理单线程的情况,借助EventLoop::runInloop()并配合EventLoopThreadPool实现了多线程TcpServer。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值