内容简介:上篇说了多线程处理的概述,这篇说说具体实现。muduo的多线程是由线程池中启动的。线程池类EventLoopThreadPool在TcpServer类中创建一个心得实例。发现在muduo中,各种类的关系基本上引用和包含即组合关系,很少有派生关系的,没有继承关系就没有虚函数的应用了。可能陈硕觉得继承关系比较复杂,耦合度太高,破坏整体设计。但是我觉得muduo中那么多不同种类的智能指针,还有基于boost或std的函数绑定,本身就够复杂的了。所以我打算有时间用c语言来改写一下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,他才是线程创建和启动真正的地方。代码如下:
void TcpServer::start() { if (started_.getAndSet(1) == 0) { threadPool_->start(threadInitCallback_); assert(!acceptor_->listenning()); loop_->runInLoop( std::bind(&Acceptor::listen, get_pointer(acceptor_))); } }
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_); } }
void Thread::start() { assert(!started_); started_ = true; // FIXME: move(func_) detail::ThreadData* data = new detail::ThreadData(func_, name_, &tid_, &latch_); if (pthread_create(&pthreadId_, NULL, &detail::startThread, data)) { started_ = false; delete data; // or no delete? LOG_SYSFATAL << "Failed in pthread_create"; } else { latch_.wait(); assert(tid_ > 0); } }
Thread的线程函数已经绑定在了EventLoopThread::threadFunc里,这个是在EventLoopThread构造函数是初始化的。代码如下:
void EventLoopThread::threadFunc() { EventLoop loop; if (callback_) { callback_(&loop); } { MutexLockGuard lock(mutex_); loop_ = &loop; cond_.notify(); } loop.loop(); //assert(exiting_); loop_ = NULL; }
这里有个问题是,主线程会加入新线程的loop实例,而这个loop实例又是在线程的线程函数里创建的。所以很显然主线程和新线程有个同步的过程,并且多个新线程之间有临界区的问题。这些是用条件变量pthread_cond_t和互斥变量mutext来实现的。
新线程线程函数在栈上申明一个EventLoop对象之后,便通知主线程了。这使得主线程返回,而新线程函数中loop开始运作循环了。那么主线程又是如何跨线程调用新线程的函数的呢?这个下篇再说。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Swoole 启动一个服务,开启了哪些进程和线程?
- JAVA线程池原理源码解析—为什么启动一个线程池,提交一个任务后,Main方法不会退出?
- Java 多线程启动为什么调用 start() 方法而不是 run() 方法?
- Java 多线程启动为什么调用 start() 方法而不是 run() 方法?
- Tomcat 7 启动分析(一)启动脚本
- java中线程安全,线程死锁,线程通信快速入门
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。