muduo多线程的启动

上篇说了多线程处理的概述,这篇说说具体实现。

muduo的多线程是由线程池中启动的。线程池类EventLoopThreadPool在TcpServer类中创建一个心得实例。发现在muduo中,各种类的关系基本上引用和包含即组合关系,很少有派生关系的,没有继承关系就没有虚函数的应用了。

可能陈硕觉得继承关系比较复杂,耦合度太高,破坏整体设计。但是我觉得muduo中那么多不同种类的智能指针,还有基于boost或std的函数绑定,本身就够复杂的了。所以我打算有时间用c语言来改写一下muduo,把那些智能指针,函数绑定等弱化,只关注网络框架本身。不过说实话,muduo本身就是基于C++的,用智能指针和函数绑定很正常,而且人家还用得非常到位。所以要想往C++方面发展,还是得精通上面的知识。我本身是搞C++,而且搞了很久,而且决定一直搞下去。不过在接触c项目,脚本语言,还有现代网络并发语言golang之后,还是觉得用C++写项目开发效率太低了,而且很难驾驭,指针就是一个雷区。精通C++的时间与其收获性价比是很低的,所以我决定以后不再画太多的时间在C++上,对于C++项目,我只是关注其框架本身。

好了废话少说。EventLoopThreadPool的start在TcpServer的start中调用,他会创建n个线程并启动,n是EventLoopThreadPool的成员变量,可以配置,也可以是0个。每个线程是EventLoopThread的实例。而EventLoopThread有个组合对象Thread,他才是线程创建和启动真正的地方。代码如下:

 1void TcpServer::start()
 2{
 3  if (started_.getAndSet(1) == 0)
 4  {
 5    threadPool_->start(threadInitCallback_);
 6
 7    assert(!acceptor_->listenning());
 8    loop_->runInLoop(
 9        std::bind(&Acceptor::listen, get_pointer(acceptor_)));
10  }
11}
 1void EventLoopThreadPool::start(const ThreadInitCallback& cb)
 2{
 3  assert(!started_);
 4  baseLoop_->assertInLoopThread();
 5
 6  started_ = true;
 7
 8  for (int i = 0; i < numThreads_; ++i)
 9  {
10    char buf[name_.size() + 32];
11    snprintf(buf, sizeof buf, "%s%d", name_.c_str(), i);
12    EventLoopThread* t = new EventLoopThread(cb, buf);
13    threads_.push_back(std::unique_ptr<EventLoopThread>(t));
14    loops_.push_back(t->startLoop());
15  }
16  if (numThreads_ == 0 && cb)
17  {
18    cb(baseLoop_);
19  }
20}
 1void Thread::start()
 2{
 3  assert(!started_);
 4  started_ = true;
 5  // FIXME: move(func_)
 6  detail::ThreadData* data = new detail::ThreadData(func_, name_, &tid_, &latch_);
 7  if (pthread_create(&pthreadId_, NULL, &detail::startThread, data))
 8  {
 9    started_ = false;
10    delete data; // or no delete?
11    LOG_SYSFATAL << "Failed in pthread_create";
12  }
13  else
14  {
15    latch_.wait();
16    assert(tid_ > 0);
17  }
18}

Thread的线程函数已经绑定在了EventLoopThread::threadFunc里,这个是在EventLoopThread构造函数是初始化的。代码如下:

 1void EventLoopThread::threadFunc()
 2{
 3  EventLoop loop;
 4
 5  if (callback_)
 6  {
 7    callback_(&loop);
 8  }
 9
10  {
11    MutexLockGuard lock(mutex_);
12    loop_ = &loop;
13    cond_.notify();
14  }
15
16  loop.loop();
17  //assert(exiting_);
18  loop_ = NULL;
19}

这里有个问题是,主线程会加入新线程的loop实例,而这个loop实例又是在线程的线程函数里创建的。所以很显然主线程和新线程有个同步的过程,并且多个新线程之间有临界区的问题。这些是用条件变量pthread_cond_t和互斥变量mutext来实现的。

新线程线程函数在栈上申明一个EventLoop对象之后,便通知主线程了。这使得主线程返回,而新线程函数中loop开始运作循环了。那么主线程又是如何跨线程调用新线程的函数的呢?这个下篇再说。

猜你喜欢

转载自blog.csdn.net/zxm342698145/article/details/81389537