EventLoopThread+EventLoopThreadPoll这两个类比较简单,放在一起说。
EventLoop线程的线程池,该类负责对线程创建,结束,可以round-robin轮询或者哈希的方式获取线程,当然也可以扩展其他负载均衡的方法。在TcpServer类中有该实例对象,生命周期和TcpServer绑定。
private:
EventLoop* baseLoop_;
std::string name_;
bool started_;
int numThreads_;
int next_;
std::vector > threads_;
std::vector loops_;
首先成员变量: baseLoop_是主线程的EventLoop,name_是名称标记,started_是线程池开启时标志位,numThreads是设置的线程总数量,next_是标记当前传递给外面的是第几个线程,threads_是线程集合vector指针,loops是循环类事件指针。
void EventLoopThreadPool::init(EventLoop* baseLoop, int numThreads)
{
numThreads_ = numThreads;
baseLoop_ = baseLoop;
}
初始化传入主线程的循环类以及创建的线程总数量。
void EventLoopThreadPool::start(const ThreadInitCallback& cb)
{
//assert(baseLoop_);
if (baseLoop_ == NULL)
return;
//assert(!started_);
if (started_)
return;
baseLoop_->assertInLoopThread();
started_ = true;
for (int i = 0; i < numThreads_; ++i)
{
char buf[128];
snprintf(buf, sizeof buf, "%s%d", name_.c_str(), i);
std::unique_ptr t(new EventLoopThread(cb, buf));
//EventLoopThread* t = new EventLoopThread(cb, buf);
//每个线程放入容器管理
loops_.push_back(t->startLoop());
threads_.push_back(std::move(t));
}
if (numThreads_ == 0 && cb)
{
//如果创建的线程为0, 则执行cb(baseLoop_);
cb(baseLoop_);//不需要建立IOThread
}
}
baseLoop_->assertInLoopThread();判断当前是不是在主线程创建,如果不是,退出程序。之后在循环中创建线程传入名称和线程序号以及回调函数,用内部容器将指针管理起来。cb这个回调函数我暂时没看明白作用是什么,之后问问大佬,再回来更新。
void EventLoopThreadPool::stop()
{
for (auto& iter : threads_)
{
iter->stopLoop();
}
}
遍历每个线程结束eventloop,并且用join结束线程。
EventLoop* EventLoopThreadPool::getNextLoop()
{
baseLoop_->assertInLoopThread();
//assert(started_);
if (!started_)
return NULL;
EventLoop* loop = baseLoop_;
if (!loops_.empty())
{
//round-robin --负载均衡
loop = loops_[next_];
++next_;
if (implicit_cast(next_)>=loops_.size())
{
next_ = 0;
}
}
return loop;
}
用轮询方式获取新线程,next做标记,分配一个线程,next加1 ,按顺序分配线程。当没有生成其他io线程时,返回主线程给外部。
线程池中分配的线程类,内部是使用std::thread,每个线程持有一个自己的EventLoop。
private:
EventLoop* loop_;
bool exiting_;
std::unique_ptr thread_;
std::mutex mutex_;
std::condition_variable cond_; //条件变量
ThreadInitCallback callback_;
loop就是每个线程持有的一个循环,exiting是线程退出标志位。thread、mutex、cond大家比较熟悉,是线程类和基本的线程同步的原语。callback_是线程初始化回调。
EventLoopThread::EventLoopThread(const ThreadInitCallback& cb,
const std::string& name/* = ""*/)
: loop_(NULL),
exiting_(false),
callback_(cb)//callback_
{
}
生命周期由EventLoopThreadPoll控制,没有特别的初始化函数,构造函数中传入自定义回调函数以及线程池中线程的名称以及序号字符。
EventLoop* EventLoopThread::startLoop()
{
//assert(!thread_.started());
//thread_.start();
thread_.reset(new std::thread(std::bind(&EventLoopThread::threadFunc, this)));
{
std::unique_lock lock(mutex_);
while (loop_ == NULL)
{
cond_.wait(lock);
}
}
return loop_;
}
void EventLoopThread::threadFunc()
{
EventLoop loop;
if (callback_)
{
callback_(&loop);
}
{
//一个一个的线程创建
std::unique_lock lock(mutex_);
loop_ = &loop;
cond_.notify_all();
}
loop.loop();
//assert(exiting_);
loop_ = NULL;
}
可以看到在startloop中线程创建了线程,之后在while(loop==NULL)判断中用条件变量使线程休眠。在threadFunc()函数中在栈上创建了一个EventLoop对象,在callback回调中可以自定义一些信息告诉线程池,用类内部的loop指针指向栈上的loop对象,释放条件变量,唤醒主线程,启动loop循环。
void EventLoopThread::stopLoop()
{
if (loop_ != NULL)
loop_->quit();
thread_->join();
}
上文中的QEventLoopThreadPoll调用的是这里的函数,结束线程常规操作,loop->quit退出线程函数,join结束销毁线程。