muduo的并发模型为one loop per thread+ threadpool。
EventLoopThread是事件循环线程,包含一个Thread对象,一个EventLoop对象。在构造函数中,把EventLoopThread::threadFunc 注册到Thread对象中(线程启动时会回调)。
EventLoopThreadPool是事件循环线程池,管理所有客户端连接,每个线程都有唯一一个事件循环,可以调用setThreadNum设置线程的数目。
EventLoopThreadPool是基于muduo库中Tcpserver这个类专门做的一个线程池,它的模式属于半同步半异步,线程池中每一个线程都有一个自己的EventLoop,而每一个EventLoop底层都是一个poll或者epoll,它利用了自身的poll或者epoll在没有事件的时候阻塞住,在有事件发生的时候,epoll监听到了事件就会去处理事件。
EventLoopThreadPool.h
#ifndef MUDUO_NET_EVENTLOOPTHREADPOOL_H
#define MUDUO_NET_EVENTLOOPTHREADPOOL_H
#include "muduo/base/noncopyable.h"
#include "muduo/base/Types.h"
#include <functional>
#include <memory>
#include <vector>
namespace muduo
{
namespace net
{
class EventLoop;
class EventLoopThread;
class EventLoopThreadPool : noncopyable
{
public:
typedef std::function<void(EventLoop*)> ThreadInitCallback;
EventLoopThreadPool(EventLoop* baseLoop, const string& nameArg);
~EventLoopThreadPool();
void setThreadNum(int numThreads) { numThreads_ = numThreads; }
void start(const ThreadInitCallback& cb = ThreadInitCallback());
// valid after calling start()
/// round-robin
EventLoop* getNextLoop();
/// with the same hash code, it will always return the same EventLoop
EventLoop* getLoopForHash(size_t hashCode);
std::vector<EventLoop*> getAllLoops();
bool started() const
{ return started_; }
const string& name() const
{ return name_; }
private:
EventLoop* baseLoop_;//主线程的EventLoop
string name_;
bool started_;
int numThreads_;//线程池中线程的数量
int next_;
std::vector<std::unique_ptr<EventLoopThread>> threads_;//线程保存在vector中
std::vector<EventLoop*> loops_;//每个EventLoopThread线程对应的EventLoop保存在loops_中
};
} // namespace net
} // namespace muduo
#endif // MUDUO_NET_EVENTLOOPTHREADPOOL_H
EventLoopThreadPool.cc
#include "muduo/net/EventLoopThreadPool.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/EventLoopThread.h"
#include <stdio.h>
using namespace muduo;
using namespace muduo::net;
EventLoopThreadPool::EventLoopThreadPool(EventLoop* baseLoop, const string& nameArg)
: baseLoop_(baseLoop),
name_(nameArg),
started_(false),
numThreads_(0),
next_(0)
{
}
EventLoopThreadPool::~EventLoopThreadPool()
{
// Don't delete loop, it's stack variable
}
/*
* 该函数会创建 numThreads_个EventLoopThread对象并运行各个线程
* 并在主线程保存创建的EventLoopThread对象和EventLoopThread线程创建的EventLoop对象。
* 如果创建的线程为0,则执行cb(baseLoop_);
*/
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());//保存线程对应的EventLoop对象
}
if (numThreads_ == 0 && cb)
{
cb(baseLoop_);
}
}
//调用该函数会按照轮流的顺序返回池里的线程的EventLoop对象指针
EventLoop* EventLoopThreadPool::getNextLoop()
{
baseLoop_->assertInLoopThread();
assert(started_);
EventLoop* loop = baseLoop_;
if (!loops_.empty())
{
// round-robin
loop = loops_[next_];
++next_;
if (implicit_cast<size_t>(next_) >= loops_.size())
{
next_ = 0;
}
}
return loop;
}
EventLoop* EventLoopThreadPool::getLoopForHash(size_t hashCode)
{
baseLoop_->assertInLoopThread();
EventLoop* loop = baseLoop_;
if (!loops_.empty())
{
loop = loops_[hashCode % loops_.size()];
}
return loop;
}
std::vector<EventLoop*> EventLoopThreadPool::getAllLoops()
{
baseLoop_->assertInLoopThread();
assert(started_);
if (loops_.empty())
{
return std::vector<EventLoop*>(1, baseLoop_);
}
else
{
return loops_;
}
}
void EventLoopThreadPool::setThreadNum(int numThreads)
{
numThreads_ = numThreads;
}
EventLoopThreadPool类的使用:
EventLoop loop;
// 创建线程池
EventLoopThreadPool pool(&loop, "");
// 设置线程个数
poll.setThreadNum(100);
// 启动线程池
poll.start();
EventLoopThread类专门创建一个线程用于执行Reactor的事件循环,当然这只是一个辅助类,没有说一定要使用它,可以根据自己的情况进行选择,你也可以不创建线程去执行事件循环,而在主线程中执行事件循环,一切根据自己的需要。
EventLoopThread(也叫IO线程)的工作流程为:
1、在主线程(暂且这么称呼)创建EventLoopThread对象。
2、主线程调用EventLoopThread.start(),启动EventLoopThread中的线程
(称为IO线程),此时主线程要等待IO线程创建完成EventLoop对象。
3、IO线程调用threadFunc创建EventLoop对象,通知主线程已经创建完成。
4、主线程返回创建的EventLoop对象。
EventLoopThread.h
#ifndef MUDUO_NET_EVENTLOOPTHREAD_H
#define MUDUO_NET_EVENTLOOPTHREAD_H
#include "muduo/base/Condition.h"
#include "muduo/base/Mutex.h"
#include "muduo/base/Thread.h"
namespace muduo
{
namespace net
{
class EventLoop;
class EventLoopThread : noncopyable
{
public:
typedef std::function<void(EventLoop*)> ThreadInitCallback;
EventLoopThread(const ThreadInitCallback& cb = ThreadInitCallback(),
const string& name = string());
~EventLoopThread();
EventLoop* startLoop();
private:
void threadFunc();
EventLoop* loop_ GUARDED_BY(mutex_);
bool exiting_;
Thread thread_;
MutexLock mutex_;
Condition cond_ GUARDED_BY(mutex_);
ThreadInitCallback callback_;
};
EventLoopThread.cc
#include "muduo/net/EventLoopThread.h"
#include "muduo/net/EventLoop.h"
using namespace muduo;
using namespace muduo::net;
EventLoopThread::EventLoopThread(const ThreadInitCallback& cb,
const string& name)
: loop_(NULL),
exiting_(false),
//将线程函数绑定为threadFunc
thread_(std::bind(&EventLoopThread::threadFunc, this), name),
mutex_(),
cond_(mutex_),
callback_(cb)
{
}
EventLoopThread::~EventLoopThread()
{
exiting_ = true;
if (loop_ != NULL) // not 100% race-free, eg. threadFunc could be running callback_.
{
// still a tiny chance to call destructed object, if threadFunc exits just now.
// but when EventLoopThread destructs, usually programming is exiting anyway.
loop_->quit();
thread_.join();
}
}
//启动一个EventLoop线程
EventLoop* EventLoopThread::startLoop()
{
assert(!thread_.started());
/*
* 在Thread::start中执行指定的线程函数,即EventLoopThread::threadFunc
* 创建EventLoop对象
*/
thread_.start();
EventLoop* loop = NULL;
{
MutexLockGuard lock(mutex_);
while (loop_ == NULL)
{
cond_.wait();// 等待创建好当前IO线程(EventLoop对象)
}
loop = loop_;
}
return loop;
}
/*
* threadFunc()运行的就是实例化一个EventLoop,并让这个EventLoop进入到loop状态。
* 运行EventLoopThread内的回调callback_
* 将刚定义好的loop传入这个回调
* 现在这个回调肯定是EventLoopThread的拥有者注册进去的
* 然后EventLoopThread也就有了一个EventLoop
* 使用loop_指向他,在这个线程中这个EventLoop一直出于loop()状态
*/
void EventLoopThread::threadFunc()
{
EventLoop loop;
if (callback_)
{
callback_(&loop);// 如果有初始化函数,就先调用初始化函数
}
{
MutexLockGuard lock(mutex_);
loop_ = &loop;
cond_.notify();//通知startLoop线程已经启动完毕
}
loop.loop(); // 事件循环
//assert(exiting_);
MutexLockGuard lock(mutex_);
loop_ = NULL;
}
one loop per thread意思是说每个线程最多只能有一个EventLoop对象。
EventLoop对象构造的时候,会检查当前线程是否已经创建了其他EventLoop对象,如果已创建,终止程序(LOG_FATAL)
EventLoop构造函数会记住本对象所属线程(threadId_)。
白色部分是外部类,对外可见,灰色部分是内部类,对外不可见
EventLoop是事件循环的抽象,Poller是I/O复用的抽象,有两个派生类,分别封装poll和epoll,这个地方是muduo库唯一使用面向对象编程风格封装的
EventLoop与Poller的关系是组合,一个EventLoop包含一个Poller,并且Poller的生存期由EventLoop来控制,EventLoop是调用Poller的poll()函数实现的
Channel是对事件的注册与响应的封装,Channel的update()函数负责注册和更新I/O的可读、可写事件,Channel的handleEvent()成员函数对所发生的I/O事件进行处理。当调用update()函数注册和更新I/O的可读、可写事件的时候,又会调用到EventLoop的updateChannel(),从而又调用了Poller的updateChannel(),相当于将Channel注册到Poller中或者将fd_文件描述符的一些可读可写事件注册到Poller中。
一个Eventloop包含多个Channel,也就是说可以用来捕捉多个通道的可读可写事件,两者之间是聚合关系,也就是说EventLoop不负责Channel的生存期的控制。Channel的生存期的控制由TcpConnection、Acceptor、Connector等等这些类来控制
Channel不拥有文件描述符,意思就是当Channel对象销毁的时候不关闭文件描述符。它和文件描述符(不是一个类)的关系是关联关系,一个Channel有一个FileDescriptor,一个EventLoop有多个FileDescriptor,用来监听多个FileDescriptor的相关事件,这个FileDescriptor是被套接字所拥有的,也就是说生存期由套接字来控制,当套接字销毁,文件描述符销毁,套接字类的析构函数调用close()
Channel是TcpConnection、Acceptor、Connector的成员,关系也是组合,生存期由这些类控制,Acceptor是对被动连接的抽象,它关注的是监听套接字的可读事件
,监听套接字的可读事件由Channel来注册,然后调用handleEvent(),从而又调用了Acceptor中的handRead(),这就是基于对象的编程思想,回调了handRead(),而不是在Channel中包含一个handRead(),然后由Acceptor继承下来。
一旦监听套接字的可读事件(注册由Channel来完成发生,就回调handRead()来响应。
Connector的handleWrite()和handleError也是由Channel来注册,Connector()是对主动连接的抽象。
一旦被动连接或者主动连接建立之后,就会得到一个已连接套接字,
已连接套接字的抽象就是TcpConnection,它会关注一些事件。
Acceptor的生存期由TcpServer来控制,Connector的生存期由TcpClient来控制。
TcpServer与TcpConnection的关系是聚合,一个TcpServer包含多TcpConnection,但是不控制TcpConnection的生存期,因为有可能客户端关闭了连接,TcpConnection对象也要跟着销毁,而TcpServer对象并不销毁,对于TcpClient也是同样的道理。
loop执行过程
EventLoop的loop()函数实际上是调用Poller的poll()函数,可能是PollPoller的poll()函数,也可能是EPollPoller的poll()函数,poll()之后就会返回一些通道activeChannels,也就是检测到通道的一些文件描述符产生了可读事件,然后调用这些activeChannels的handleEvent(),接着调用回调函数(调用比如TcpConnection或者Acceptor或者Connector里面的回调函数)
enableReading():关注可读事件,把通道注册到EventLoop,从而注册到EventLoop所持有的poll对象中
能够poll之前,肯定要注册一些事件,注册事件是由enableReading()、enableWriting()发起的,
void enableReading() { events_ |= kReadEvent; update(); }
它会调用Update(),进而又调用EventLoop的UpdateChannel(),进而调用Poller的UpdateChannel()
EventLoop.cc
//事件循环,不能跨线程调用
//只能在创建该对象的线程中调用
void EventLoop::loop()
{
assert(!looping_);
assertInLoopThread(); //断言处于创建该对象的线程中
looping_ = true;
quit_ = false; // FIXME: what if someone calls quit() before loop() ?
LOG_TRACE << "EventLoop " << this << " start looping";
while (!quit_)
{
activeChannels_.clear(); //首先清零
pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_); //调用poll返回活动的通道,有可能是唤醒返回的
++iteration_;
if (Logger::logLevel() <= Logger::TRACE)
{
printActiveChannels(); //日志登记,日志打印
}
// TODO sort channel by priority
eventHandling_ = true; //true
for (ChannelList::iterator it = activeChannels_.begin();
it != activeChannels_.end(); ++it) //遍历通道来进行处理
{
currentActiveChannel_ = *it;
currentActiveChannel_->handleEvent(pollReturnTime_);
}
currentActiveChannel_ = NULL; //处理完了赋空
eventHandling_ = false; //false
//I/O线程设计比较灵活,通过下面这个设计也能够进行计算任务,否则当I/O不是很繁忙的时候,这个I/O线程就一直处于阻塞状态。
//我们需要让它也能执行一些计算任务
doPendingFunctors(); //处理用户回调任务
}
LOG_TRACE << "EventLoop " << this << " stop looping";
looping_ = false;
}