前文【muduo学习笔记:net部分之EventLoop】介绍了EventLoop的原理实现和基本使用。任何一个创建并运行了EventLoop
的线程就是IO线程,在任何线程中都可以创建运行EventLoop
。在一个程序中,可能不止一个IO线程,IO线程也比一定是主线程。为方便多线程使用,EventloopThread
类封装了IO线程,该类创建了一个线程,并在线程函数中创建了一个EventLoop
对象并执行事件循环。
EventThreadPool
是封装了多个EventLoop
的线程池,主要用在多线程TCP服务端。在单线程TCP服务端中,EventLoop
既用于接受新的连接、也用于执行IO。而多线程中TCP服务端创建的EventLoop
仅用于接受新的TCP连接,IO事件处理全部交由其他EventLoop
执行,IO事件处理的分派策略后续介绍。
1、EventLoopThread
实现简单,封装了一个EventLoop
,提供接口开始执行事件循环、获取创建的EventLoop
对象。
1.1、代码实现
(1)构造初始化、析构
初始化成员变量,创建一个线程用于执行事件循环。
EventLoopThread::EventLoopThread(const ThreadInitCallback& cb,
const string& name)
: loop_(NULL), // 指向当前线程创建的EventLoop对象
exiting_(false), // 退出线程标志位
thread_(std::bind(&EventLoopThread::threadFunc, this), name), // 线程
mutex_(), // 互斥锁
cond_(mutex_), // 用于保证初始化成功
callback_(cb) // 线程初始化的回调函数
{
}
// 析构,退出事件循环,关闭线程
EventLoopThread::~EventLoopThread()
{
exiting_ = true;
if (loop_ != NULL) {
loop_->quit();
thread_.join();
}
}
(2)启动事件循环
启动线程,执行线程函数,阻塞等到线程函数通知,最后返回创建的EvnetLoop对象指针。
EventLoop* EventLoopThread::startLoop()
{
assert(!thread_.started());
thread_.start(); // 启动线程,执行线程函数
EventLoop* loop = NULL;
{
MutexLockGuard lock(mutex_);
while (loop_ == NULL){
cond_.wait(); // 保证线程函数准备工作完成,EventLoop指针对象不为空
}
loop = loop_;
}
return loop;
}
(3)线程函数
void EventLoopThread::threadFunc()
{
EventLoop loop; // 线程中创建一个EventLoop对象
if (callback_){
callback_(&loop); // 执行初始化回调函数
}
{
MutexLockGuard lock(mutex_);
loop_ = &loop;
cond_.notify(); // 初始化成功,通知用户启动成功
}
loop.loop(); // 线程中执行事件循环
//assert(exiting_);
MutexLockGuard lock(mutex_);
loop_ = NULL;
}
1.2、测试
void print(EventLoop* p = NULL)
{
printf("print: pid = %d, tid = %d, loop = %p\n", getpid(), CurrentThread::tid(), p);
}
void quit(EventLoop* p)
{
print(p);
p->quit();
}
int main()
{
print();
{
EventLoopThread thr1; // never start
}
{
// dtor calls quit()
EventLoopThread thr2;
EventLoop* loop = thr2.startLoop();
loop->runInLoop(std::bind(print, loop));
CurrentThread::sleepUsec(500 * 1000);
}
{
// quit() before dtor
EventLoopThread thr3;
EventLoop* loop = thr3.startLoop();
loop->runInLoop(std::bind(quit, loop));
CurrentThread::sleepUsec(500 * 1000);
}
}
在主线程中创建三个ThreadLoopThread,但仅启动了后两个。
print: pid = 1588, tid = 1588, loop = (nil)
print: pid = 1588, tid = 1589, loop = 0x7fbcc527f9c0
print: pid = 1588, tid = 1590, loop = 0x7fbcc527f9c0
2、EventLoopThreadPool
EventThreadPool
是封装了多个EventLoop的线程池,管理所有客户端上的IO事件,每个线程都有唯一一个事件循环。主线程的EventLoop负责新的客户端连接,线程池中的EventLoop负责客户端的IO事件,客户端IO事件的分配按照一定规则执行。
当线程池中的线程数量为0时,那么客户端共用服务端的EventLoop,退化成单线程模式了。
2.1、代码实现
头文件如下
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());
// 启动后有效,使用round-robin策略获取线程池中的EventLoop对象
EventLoop* getNextLoop();
// 使用固定的hash方法,获取相同的线程对象
EventLoop* getLoopForHash(size_t hashCode);
std::vector<EventLoop*> getAllLoops(); // 返回所有EvenetLoop,含baseloop
bool started() const { return started_; }
const string& name() const { return name_; }
private:
EventLoop* baseLoop_; // Acceptor所属EventLoop
string name_; // 线程池名称
bool started_; // 是否已经启动
int numThreads_; // 线程数
int next_; // 新连接到来,所选择的EventLoop对象下标
std::vector<std::unique_ptr<EventLoopThread>> threads_; // IO线程列表
std::vector<EventLoop*> loops_; // EventLoop列表
};
实现如下
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
}
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线程。在进入事件循环之前。会调用cb
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_); // 仅仅有一个EventLoop。在这个EventLoop进入事件循环之前,调用cb
}
}
EventLoop* EventLoopThreadPool::getNextLoop()
{
baseLoop_->assertInLoopThread();
assert(started_);
EventLoop* loop = baseLoop_;// 假设loops_为空,则loop指向baseLoop_
if (!loops_.empty()){ // 假设不为空,依照round-robin的调度方式选择一个EventLoop
// 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_;
}
}
2.2、测试
测试代码如下:
void print(EventLoop* p = NULL)
{
printf("main(): pid = %d, tid = %d, loop = %p\n", getpid(), CurrentThread::tid(), p);
}
void init(EventLoop* p)
{
printf("init(): pid = %d, tid = %d, loop = %p\n", getpid(), CurrentThread::tid(), p);
}
int main()
{
print();
EventLoop loop;
loop.runAfter(11, std::bind(&EventLoop::quit, &loop));
{
printf("Single thread %p:\n", &loop);
EventLoopThreadPool model(&loop, "single");
model.setThreadNum(0);
model.start(init);
assert(model.getNextLoop() == &loop);
assert(model.getNextLoop() == &loop);
assert(model.getNextLoop() == &loop);
}
{
printf("Another thread:\n");
EventLoopThreadPool model(&loop, "another");
model.setThreadNum(1);
model.start(init);
EventLoop* nextLoop = model.getNextLoop();
nextLoop->runAfter(2, std::bind(print, nextLoop));
assert(nextLoop != &loop);
assert(nextLoop == model.getNextLoop());
assert(nextLoop == model.getNextLoop());
::sleep(3);
}
{
printf("Three threads:\n");
EventLoopThreadPool model(&loop, "three");
model.setThreadNum(3);
model.start(init);
EventLoop* nextLoop = model.getNextLoop();
nextLoop->runInLoop(std::bind(print, nextLoop));
assert(nextLoop != &loop);
assert(nextLoop != model.getNextLoop());
assert(nextLoop != model.getNextLoop());
assert(nextLoop == model.getNextLoop());
}
loop.loop();
}
运行结果
main(): pid = 2228, tid = 2228, loop = (nil)
Single thread 0x7fffc96d04a0:
init(): pid = 2228, tid = 2228, loop = 0x7fffc96d04a0
Another thread:
init(): pid = 2228, tid = 2229, loop = 0x7fa4fc87f9c0
main(): pid = 2228, tid = 2229, loop = 0x7fa4fc87f9c0
Three threads:
init(): pid = 2228, tid = 2271, loop = 0x7fa4fc87f9c0
init(): pid = 2228, tid = 2272, loop = 0x7fa4f7fef9c0
init(): pid = 2228, tid = 2273, loop = 0x7fa4f77df9c0
main(): pid = 2228, tid = 2271, loop = 0x7fa4fc87f9c0