muduo网络库源码详解(3) 分析muduo的EventLoopThreadPool的多线程机制

muduo网络库源码详解(3) 分析muduo的EventLoopThreadPool的多线程机制

主要参考了陈硕的书《Linux多线程服务端编程》,结合自己的逻辑摘了一些重要的的句子和函数、数据成员进行分析,
本文代码用C++11的多线程机制改写了muduo,没用muduo的base库,但原理都是一样的。

EchoServer的主函数

int main()
{
  muduo::net::EventLoop loop; // 构造一个loop
  muduo::net::InetAddress listenAddr(8888); // sockaddr_in的封装,服务地址未填写默认本地
  EchoServer server(&loop, listenAddr); // TcpServer的初始化、构造函数里 手动绑定回调函数
  server.start();
  loop.loop();
}

上一篇讲到了TcpServer.start(),本文接着以TcpServer::start为入口分析多线程机制

// 开启服务器监听   loop.loop()
void TcpServer::start()
{
    if (started_++ == 0) // 防止一个TcpServer对象被start多次
    {
        // 用户设置的回调函数threadInitCallback_,没设置为空
        threadPool_->start(threadInitCallback_); // 启动threadPool_
        
        loop_->runInLoop(std::bind(&Acceptor::listen, acceptor_.get())); // 在当前线程开启监听
    }
}

总览图 —— 细节都在图里了

红色的线标识的是 回调函数(用户设置的)的传递的时机
由TcpServer的构造时 构造的EventLoopThreadPool负责 muduo 多线程机制

image-20211222141659542

图可放大
以下为详细的代码

EventLoopThreadPool::start

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); // 初始化一个EventLoopThread
        threads_.push_back(std::unique_ptr<EventLoopThread>(t));
        loops_.push_back(t->startLoop()); // 底层创建线程,绑定一个新的EventLoop,并返回该loop的地址
    }

    // 整个服务端只有一个线程,运行着baseloop
    if (numThreads_ == 0 && cb)
    {
        cb(baseLoop_);
    }
}

EventLoopThread::EventLoopThread

EventLoopThread::EventLoopThread(const ThreadInitCallback &cb, 
        const std::string &name)
        : loop_(nullptr)
        , exiting_(false)
        , thread_(std::bind(&EventLoopThread::threadFunc, this), name) // 绑定threadFunc
        , mutex_()
        , cond_()
        , callback_(cb)
{
}
Thread
void Thread::start() // 一个Thread对象。记录的就是一个新线程的详细信息
{
    started_ = true;
    sem_t sem;
    sem_init(&sem, false, 0);

    // 开启线程
    thread_ = std::shared_ptr<std::thread>(new std::thread([&](){
        tid_ = CurrentThread::tid();
        sem_post(&sem);
        // 开启一个新线程,专门执行该线程函数
        func_(); // 
    }));

    // 这里必须等待获取上面新创建的线程的tid值
    sem_wait(&sem);
}

EventLoopThread::startLoop

EventLoop* EventLoopThread::startLoop()
{
    thread_.start(); // 启动底层的新线程

    EventLoop *loop = nullptr;
    {
        std::unique_lock<std::mutex> lock(mutex_);
        while ( loop_ == nullptr )
        {
            cond_.wait(lock);
        }
        loop = loop_;
    }
    return loop;
}
void Thread::start
void Thread::start() // 一个Thread对象。记录的就是一个新线程的详细信息
{
    started_ = true;
    sem_t sem;
    sem_init(&sem, false, 0);

    // 开启线程
    thread_ = std::shared_ptr<std::thread>(new std::thread([&](){
        tid_ = CurrentThread::tid();
        sem_post(&sem);
        // 开启一个新线程,专门执行该线程函数
        func_();
    }));

    // 这里必须等待获取上面新创建的线程的tid值
    sem_wait(&sem);
}
EventLoopThread::threadFunc
// 下面这个方法,是在单独的新线程里面运行的
void EventLoopThread::threadFunc()
{
    EventLoop loop; // 创建一个独立的eventloop,和上面的线程是一一对应的,one loop per thread

    if (callback_) // 执行ThreadInitCallback
    {
        callback_(&loop);
    }

    {
        std::unique_lock<std::mutex> lock(mutex_);
        loop_ = &loop;
        cond_.notify_one(); // 阻塞
    }

    loop.loop(); // EventLoop loop  => Poller.poll
    std::unique_lock<std::mutex> lock(mutex_);
    loop_ = nullptr;
}
void EventLoop::loop()
// 开启事件循环
void EventLoop::loop()
{
    looping_ = true;
    quit_ = false;

    LOG_INFO("EventLoop %p start looping \n", this);

    while(!quit_)
    {
        activeChannels_.clear();
        // 监听两类fd   一种是client的fd,一种wakeupfd
        pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);
        for (Channel *channel : activeChannels_)
        {
            // Poller监听哪些channel发生事件了,然后上报给EventLoop,通知channel处理相应的事件
            channel->handleEvent(pollReturnTime_);
        }
        // 执行当前EventLoop事件循环需要处理的回调操作
        /**
         * IO线程 mainLoop accept fd《=channel subloop
         * mainLoop 事先注册一个回调cb(需要subloop来执行)    wakeup subloop后,执行下面的方法,执行之前mainloop注册的cb操作
         */ 
        doPendingFunctors();
    }

    LOG_INFO("EventLoop %p stop looping. \n", this);
    looping_ = false;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值