Muduo网络库源代码分析(四)EventLoopThread和EventLoopThreadPool的封装
muduo的并发模型为one loop per thread+ threadpool。为了方便使用,muduo封装了EventLoop和Thread为EventLoopThread,为了方便使用线程池,又把EventLoopThread封装为EventLoopThreadPool。
所以这篇博文并没有涉及到新奇的技术。可是也有一些封装和逻辑方面的注意点须要我们去分析和理解。
EventLoopThread
不论什么一个线程,仅仅要创建并执行了EventLoop,就是一个IO线程。 EventLoopThread类就是一个封装好了的IO线程。
EventLoopThread的工作流程为:
1、在主线程创建EventLoopThread对象。
2、主线程调用EventLoopThread.start(),启动EventLoopThread中的线程(称为IO线程),而且主线程要等待IO线程创建完毕EventLoop对象。
3、IO线程调用threadFunc创建EventLoop对象。通知主线程已经创建完毕。
4、主线程返回创建的EventLoop对象。
EventLoopThread.h
class EventLoopThread : boost::noncopyable
{
public:
typedef boost::function<void(EventLoop*)> ThreadInitCallback; EventLoopThread(const ThreadInitCallback& cb = ThreadInitCallback());
~EventLoopThread();
EventLoop* startLoop(); // 启动线程,该线程就成为了IO线程 private:
void threadFunc(); // 线程函数 EventLoop* loop_; // loop_指针指向一个EventLoop对象
bool exiting_;
Thread thread_;
MutexLock mutex_;
Condition cond_;
ThreadInitCallback callback_; // 回调函数在EventLoop::loop事件循环之前被调用
};
EventLoopThread.cc
EventLoopThread::EventLoopThread(const ThreadInitCallback& cb)
: loop_(NULL),
exiting_(false),
thread_(boost::bind(&EventLoopThread::threadFunc, this)),
mutex_(),
cond_(mutex_),
callback_(cb)
{
} EventLoopThread::~EventLoopThread()
{
exiting_ = true;
loop_->quit(); // 退出IO线程,让IO线程的loop循环退出。从而退出了IO线程
thread_.join(); //等待线程退出
} EventLoop* EventLoopThread::startLoop()
{
assert(!thread_.started());
thread_.start();//线程启动,调用threadFunc() {
MutexLockGuard lock(mutex_);
while (loop_ == NULL)
{
cond_.wait();//须要等待EventLoop对象的创建
}
} return loop_;
} void EventLoopThread::threadFunc()
{
EventLoop loop; if (callback_)
{
callback_(&loop);
} {
MutexLockGuard lock(mutex_);
// loop_指针指向了一个栈上的对象,threadFunc函数退出之后。这个指针就失效了
// threadFunc函数退出,就意味着线程退出了,EventLoopThread对象也就没有存在的价值了。
// 因而不会有什么大的问题
loop_ = &loop;
cond_.notify(); //创建好,发送通知
} loop.loop();// 会在这里循环,直到EventLoopThread析构。此后不再使用loop_訪问EventLoop了
//assert(exiting_);
}
測试程序:
#include <muduo/net/EventLoop.h>
#include <muduo/net/EventLoopThread.h> #include <stdio.h> using namespace muduo;
using namespace muduo::net; void runInThread()
{
printf("runInThread(): pid = %d, tid = %d\n",
getpid(), CurrentThread::tid());
} int main()
{
printf("main(): pid = %d, tid = %d\n",
getpid(), CurrentThread::tid()); EventLoopThread loopThread;
EventLoop* loop = loopThread.startLoop();
// 异步调用runInThread,即将runInThread加入到loop对象所在IO线程,让该IO线程运行
loop->runInLoop(runInThread);
sleep(1);
// runAfter内部也调用了runInLoop。所以这里也是异步调用
loop->runAfter(2, runInThread);
sleep(3);
loop->quit(); printf("exit main().\n");
}
对调用过程进行分析:(查看日志)
主线程调用 loop->runInLoop(runInThread);
因为主线程(不是IO线程)调用runInLoop。 故调用queueInLoop()
将runInThead
加入到队列,然后wakeup() IO线程。IO线程在doPendingFunctors()
中取loop->runAfter()
要唤醒一下,此时仅仅是运行runAfter()
加入了一个2s的定时器, 2s超时。timerfd_
可读,先handleRead()一下然后运行回调函数runInThread()。
那为什么exit main()
之后wakeupFd_
还会有可读事件呢?那是由于EventLoopThead
栈上对象析构,在析构函数内 loop_ ->quit(),
因为不是在IO线程调用quit(),故也须要唤醒一下。IO线程才干从poll
返回,这样再次循环推断 while (!quit_)
就能退出IO线程。
EventLoopThreadPool
muduo的线程模型:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
muduo的思想时eventLoop+thread pool。为了更方便使用,将EventLoopThread做了封装。main reactor能够创建sub reactor,并发一些任务分发到sub reactor中去。EventLoopThreadPool的思想比較简单,用一个main reactor创建EventLoopThreadPool。在EventLoopThreadPool中将EventLoop和Thread绑定,能够返回EventLoop对象来使用EventLoopThreadPool中的Thread。
EventLoopThreadPool.h
class EventLoopThreadPool : boost::noncopyable
{
public:
typedef boost::function<void(EventLoop*)> ThreadInitCallback; EventLoopThreadPool(EventLoop* baseLoop);
~EventLoopThreadPool();
void setThreadNum(int numThreads) { numThreads_ = numThreads; }
void start(const ThreadInitCallback& cb = ThreadInitCallback());
EventLoop* getNextLoop(); private: EventLoop* baseLoop_; // 与Acceptor所属EventLoop同样
bool started_;
int numThreads_; // 线程数
int next_; // 新连接到来。所选择的EventLoop对象下标
boost::ptr_vector<EventLoopThread> threads_; // IO线程列表
std::vector<EventLoop*> loops_; // EventLoop列表
};
EventLoopThreadPool.cc
EventLoopThreadPool::EventLoopThreadPool(EventLoop* baseLoop)
: baseLoop_(baseLoop),
started_(false),
numThreads_(0),
next_(0)
{
} EventLoopThreadPool::~EventLoopThreadPool()
{
// Don't delete loop, it's stack variable
} void EventLoopThreadPool::start(const ThreadInitCallback& cb)
{
assert(!started_);
baseLoop_->assertInLoopThread(); started_ = true; for (int i = 0; i < numThreads_; ++i)
{
EventLoopThread* t = new EventLoopThread(cb);
threads_.push_back(t);
loops_.push_back(t->startLoop()); // 启动EventLoopThread线程。在进入事件循环之前。会调用cb
}
if (numThreads_ == 0 && cb)
{
// 仅仅有一个EventLoop。在这个EventLoop进入事件循环之前,调用cb
cb(baseLoop_);
}
} EventLoop* EventLoopThreadPool::getNextLoop()
{
baseLoop_->assertInLoopThread();
EventLoop* loop = baseLoop_; // 假设loops_为空,则loop指向baseLoop_
// 假设不为空,依照round-robin(RR。轮叫)的调度方式选择一个EventLoop
if (!loops_.empty())
{
// round-robin
loop = loops_[next_];
++next_;
if (implicit_cast<size_t>(next_) >= loops_.size())
{
next_ = 0;
}
}
return loop;
}
mainReactor关注监听事件,已连接套接字事件轮询给线程池中的subReactors 处理,一个新的连接相应一个subReactor
我们採用round-robin(RR,轮叫)的调度方式选择一个EventLoop,也就是getNextLoop函数。极端情况下,线程池中个数为0时,那么新的连接交给mainReactor。这样就退化成单线程的模式。
Muduo网络库源代码分析(四)EventLoopThread和EventLoopThreadPool的封装的更多相关文章
- Muduo网络库源代码分析(六)TcpConnection 的生存期管理
TcpConnection是使用shared_ptr来管理的类,由于它的生命周期模糊.TcpConnection表示已经建立或正在建立的连接.建立连接后,用户仅仅须要在上层类如TcpServer中设置 ...
- muduo网络库学习笔记(四) 通过eventfd实现的事件通知机制
目录 muduo网络库学习笔记(四) 通过eventfd实现的事件通知机制 eventfd的使用 eventfd系统函数 使用示例 EventLoop对eventfd的封装 工作时序 runInLoo ...
- muduo网络库架构总结
目录 muduo网络库简介 muduo网络库模块组成 Recator反应器 EventLoop的两个组件 TimerQueue定时器 Eventfd Connector和Acceptor连接器和监听器 ...
- 长文梳理muduo网络库核心代码、剖析优秀编程细节
前言 muduo库是陈硕个人开发的tcp网络编程库,支持Reactor模型,推荐大家阅读陈硕写的<Linux多线程服务端编程:使用muduo C++网络库>.本人前段时间出于个人学习.找工 ...
- muduo 网络库学习之路(一)
前提介绍: 本人是一名大三学生,主要使用C++开发,兴趣是高性能的服务器方面. 网络开发离不开网络库,所以今天开始学一个新的网络库,陈老师的muduo库 我参考的书籍就是陈老师自己关于muduo而编著 ...
- 陈硕 - Linux 多线程服务端编程 - muduo 网络库作者
http://chenshuo.com/book/ Muduo网络库源码分析(一) EventLoop事件循环(Poller和Channel)http://blog.csdn.net/nk_test/ ...
- Muduo网络库实战(二):实现服务器与客户端的连接
1. 方案的确定 1)基本需求 用户1000+, IO压力不大: 多个客户端打开网站,输入查询字符串strclient,发送给服务器=>服务器接收客户端发过来的数据并处理,将结果返回给客户端: ...
- muduo网络库使用心得
上个月看了朋友推荐的mudo网络库,下完代码得知是国内同行的开源作品,甚是敬佩.下了mudo使用手冊和035版的代码看了下结构,感觉是一个比較成熟并且方便使用的网络库.本人手头也有自己的网络库,尽管不 ...
- muduo网络库学习笔记(五) 链接器Connector与监听器Acceptor
目录 muduo网络库学习笔记(五) 链接器Connector与监听器Acceptor Connector 系统函数connect 处理非阻塞connect的步骤: Connetor时序图 Accep ...
随机推荐
- linux 通过两个网卡,连接不同的不同的网段
linux数据包转发功能 A:192.168.xxx.xxx B:172.24.xxx.xxx, 从而实现了A网段和B网段的互通. 原因Linux机器可以通过设置实现数据包的转发功能. #echo ...
- ES6里关于数字的拓展
一.指数运算符 ES6引入的唯一一个JS语法变化是求幂运算符,它是一种将指数应用于基数的数学运算.JS已有的Math.pow()方法可以执行求幂运算,但它也是为数不多的需要通过方法而不是正式的运算符来 ...
- functor
I thought it would be easy and convenient to define a small functor and perform a customized sort on ...
- 机器学习第4课:多变量线性回归(Linear Regression with Multiple Variables)
4.1 多维特征 目前为止,我们探讨了单变量/特征的回归模型,现在我们对房价模型增加更多的特征, 例如房间数楼层等,构成一个含有多个变量的模型,模型中的特征为(x1,x2,...,xn).
- 【Docker】Docker管理平台 Rancher ---- 你应该学学Rancher是怎么做容器的管理的
Elasticsearch is a Lucene-based search engine developed by the open-source vendor, elastic. With pri ...
- 固态硬盘(Solid State Drives)
固态硬盘(Solid State Drives) 学习了:https://baike.baidu.com/item/%E5%9B%BA%E6%80%81%E7%A1%AC%E7%9B%98/45351 ...
- SQL Server 2008 导入MDF数据库文件
1.将.MDF和.LDF文件拷到SQL Server2008数据目录,如:\\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\ ...
- Linux Oracle数据库的安装
// 注释 # root用户 $oracle用户 1. 关闭安全措施 # chkconfig iptables off // 永久关闭防火墙 # serviceiptables stop // ...
- java编程思想读书笔记 第十二章 通过异常处理错误(下)
1.异常的限制 当覆盖方法的时候,仅仅能抛出在基类方法的异常说明里列出的那些异常. 这意味着,当基类使用的代码应用到其派生类对象的时候,一样能够工资,异常也不例外. 以下的样例是在编译时施加在异常上面 ...
- 算法练习--ABCD*E=DCBA
//1.ABCD * E = DBCA//2.A,B,C,D {0-9} , E {1-9}//3.A,B,C,D,E all different var existSameN = function ...