[muduo网络库]——从EchoServer开始启动服务器(剖析muduo网络库核心部分、设计思想)

接着上文,我们简单使用muduo库搭建了Echo服务器,[muduo网络库]——使用muduo库搭建Echo服务器(剖析muduo网络库核心部分、设计思想)接下来,我们来看看main函数中,如何使用server.start(),启动服务器,内部又发生了什么呢?

启动服务器

  • server.start();,实际是server_.start();,并且这里的server_属于TcpServer server_;,所以最底层调用了TcpServer::start()
void TcpServer::start()
{
    if(started_++ == 0) //防止一个TcpServer对象被start多次
    {
        threadPool_->start(threadInitCallback_);
        loop_->runInLoop(std::bind(&Acceptor::listen,acceptor_.get()));
    }
}
  • 此时,主线程调用Acceptor::listen( )
void Acceptor::listen()
{
    listenning_ = true;
    acceptSocket_.listen(); //listen 
    acceptChannel_.enableReading(); //acceptChannel_=> Poller
}
  • 底层调用了系统的listen() 监听服务器套接字,以及将acceptChannel_注册到mainLoop上监听它的可读事件(新用户连接事件)
  • 接着threadPool_->start(threadInitCallback_);
  • 这里的started_初始值是0,if(started_++ == 0) 就是防止一个TcpServer对象被start多次,threadPool_std::属于shared_ptr<EventLoopThreadPool> threadPool_; 接着会调用EventLoopThreadPool::start(),cb=threadInitCallback_;
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_);//执行
    }
}
  1. 注意在搭建EchoServer时,我们设置的subloop线程数量为3,名字给为了EchoServer-01,所以在这里我们进入for循环;
  2. 创建一个EventLoopThread对象,传入了新的名字和对应的回调,加入线程池内线程集合,调用了EventLoopThread::startLoop(),并在构造函数中,将thread_绑定了回调函数threadFunc
EventLoop* EventLoopThread::startLoop()
{
    thread_.start(); //启动底层新线程
    EventLoop *loop =nullptr; //当startLoop时,才开始创建新线程,线程里面有一个loop
    {
        std::unique_lock<std::mutex> lock(mutex_);
        while(loop_==nullptr)
        {
            cond_.wait(lock); //须要等待EventLoop对象的创建
        }
        loop = loop_;        //IO线程创建loop_赋给主线程
    }
    return loop; //主线程返回IO线程创建的EventLoop对象
}

此时,子线程开始执行EventLoopThread::threadFunc()函数

void EventLoopThread::threadFunc()
{
    ////IO线程也要创建EventLoop对象,还要通知主线程已经创建完毕
    EventLoop loop;//创建一个独立的Eventloop,和上面的线程是一一对应的 one loop per thread

    if(callback_)
    {
        callback_(&loop);  //将定义好的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;
}
  1. 此时子线程正则执行EventLoopThread::threadFunc,主线程继续执行EventLoopThread::startLoop(),并通过条件变量等待loop_不为空,也即是等待子线程创建一个EventLoop 对象;
  2. 子线程会创建一个loop局部变量,由于loop是在子线程中创建的,loop的threadId_成员就会在EventLoop的构造函数中被初始化为子线程的tid。子线程创建好loop后,把自己的地址传给loop_,然后唤醒主线程,通知其自己已经创建好了(绑定了)EventLoop,这样,主线程也就会解除阻塞,顺利返回子线程绑定的EventLoop。
  3. 在子线程中,执行loop.loop();,子线程进入了死循环,只要死循环不退出EventLoopThread::threadFunc也不会继续往下执行。
void EventLoop::loop()
{
    looping_ = true;
    quit_ = false;

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

    while(!quit_)
    {
        activeChannels_.clear();
        pollReturnTime_ = poller_->poll(kPollTimeMs,&activeChannels_);
        for(Channel *channel : activeChannels_)
        {
            channel->handleEvent(pollReturnTime_);
        }
        doPendingFunctors();
    }
    LOG_INFO("EventLoop %p stop looping,\n",this);
    looping_ = false;
}

调用EPollPoller::poll,此时,由于还没有任何TCP连接到来,子线程的poller上没有注册任何sockfd上的感兴趣事件,所以会阻塞在epoll_wait,也就是poller_->poll
到此,main函数也就是主线程的server.start()就执行结束了,接下来主线程继续执行loop.loop(),同样,主线程也阻塞在了poller_->poll中,可读事件。

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

好了~ 有关于服务器的启动,我们就分析到这,启动服务器以后,所有线程都处于阻塞在poller_->poll上,等待新连接的到来,那么连接是怎么建立的,我们下一节见~
  • 14
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值