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 ...
随机推荐
- Android-Binder 简析
前言 对于Android来说,Binder的重要性怎么说都不为过.不管是我们的四大组件Activity.Service.BroadcastReceiver.ContentProvider,还是经常在应 ...
- C++之类成员所占内存大小问题总结
1.空类所占字节数为1,可见代码如下 #include <iostream> using namespace std; class Parent { }; class Child:publ ...
- SparkSQL的3种Join实现
引言 Join是SQL语句中的常用操作,良好的表结构能够将数据分散在不同的表中,使其符合某种范式,减少表冗余.更新容错等.而建立表和表之间关系的最佳方式就是Join操作. 对于Spark来说有3中Jo ...
- asp.net限制用户登录错误次数
很经常在登录一个网站的时候看到,如果你登录的时候输入的账号密码错误超过三次就被锁定,然后等一段时间才能继续登录,最最经常使用的就是银行系统啦~~ 该功能处理流程如下: string uid = Req ...
- WinSock基本知识
这里不打算系统地介绍socket或者WinSock的知识.首先介绍WinSock API函数,讲解阻塞/非阻塞的概念:然后介绍socket的使用. WinSock API Socket接口是网络编程( ...
- Splay树(多操作)——POJ 3580 SuperMemo
相应POJ题目:点击打开链接 SuperMemo Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 11309 Accept ...
- 标准库Queue的实现
跟上篇实现stack的思路一致,我增加了一些成员函数模板,支持不同类型的Queue之间的复制和赋值. 同时提供一个异常类. 代码如下: #ifndef QUEUE_HPP #define QUEUE_ ...
- 禁止右键,Ctrl+A,Ctrl+C,Ctrl+V来禁止复制内容,IE网页另存可禁止,但对火狐浏览器没有用的
禁止右键,Ctrl+A,Ctrl+C,Ctrl+V来禁止复制内容,IE网页另存可禁止,但对火狐浏览器没有用的. 代码如下:(开发了左键选择,方便阅读)<!DOCTYPE HTML PUBLIC ...
- Notepad++搭配MinGW 配置编译运行C/C++
1. Notepad++与Dev-Cpp都能编译运行, 环境变量的设置: 在PATH中加入"Dev-Cpp的MinGW64下的bin", 这是寻找gcc编译器的路径. 新建LIBR ...
- (八)Thymeleaf的 th:* 属性之—— 模板布局& th:with& 属性优先级
3.7 模板布局 模板名称:layout.html 3.7.1 th:fragment e.g.模板名为footer.html页面body部分如下: <body> <div th:f ...