muduo/base/ThreadPool.h
muduo/base/ThreadPool.cc
设计理念
1.依据threadNum决定Task执行线程
void ThreadPool::start(int numThreads)
{
assert(threads_.empty());
running_ = true;
threads_.reserve(numThreads);
for (int i = 0; i < numThreads; ++i)
{
char id[32];
snprintf(id, sizeof id, "%d", i+1);
threads_.emplace_back(new muduo::Thread(
std::bind(&ThreadPool::runInThread, this), name_+id));
threads_[i]->start();
}
if (numThreads == 0 && threadInitCallback_)
{
threadInitCallback_();
}
}
没有指定线程个数时,由调用线程自己执行Task
void ThreadPool::run(Task task)
{
// 注意这里: 没有指定线程个数
if (threads_.empty())
{
task();
}
else
{
MutexLockGuard lock(mutex_);
while (isFull() && running_)
{
notFull_.wait();
}
if (!running_) return;
assert(!isFull());
queue_.push_back(std::move(task));
notEmpty_.notify();
}
}
这一点在muduo的
EventLoopThreadPool
中也能体现, 当没有指定线程个数时,所以事件都将投放到baseLoop
2.任务队列满载将阻塞调用线程,而非返回错误
这里muduo采用了两个条件变量处理线程池主要逻辑,
当任务队列为empty,通过notEmpty(条件变量)阻塞工作线程.
当任务队列为full,通过notFull(条件变量)阻塞调用线程
void ThreadPool::stop()
{
{
MutexLockGuard lock(mutex_);
running_ = false;
notEmpty_.notifyAll();
//
notFull_.notifyAll();
}
for (auto& thr : threads_)
{
thr->join();
}
}
void ThreadPool::run(Task task)
{
if (threads_.empty())
{
task();
}
else
{
MutexLockGuard lock(mutex_);
// 注意这里: 满载将阻塞调用线程
while (isFull() && running_)
{
notFull_.wait();
}
if (!running_) return;
assert(!isFull());
queue_.push_back(std::move(task));
notEmpty_.notify();
}
}
3.stop策略
-
stop后所有工作线程被唤醒放弃执行滞留在任务队列的工作
-
因满载而阻塞的调用线程立即被唤醒并放弃添加任务
void ThreadPool::run(Task task)
{
if (threads_.empty())
{
task();
}
else
{
MutexLockGuard lock(mutex_);
while (isFull() && running_)
{
notFull_.wait();
}
// 注意 running_: 满载将阻塞调用线程
if (!running_) return;
assert(!isFull());
queue_.push_back(std::move(task));
notEmpty_.notify();
}
}
runInThread: 工作线程主循环
void ThreadPool::runInThread()
{
try
{
if (threadInitCallback_)
{
threadInitCallback_();
}
// 注意 running_ : 工作线程执行完手头任务后立刻退出
while (running_)
{
Task task(take());
if (task)
{
task();
}
}
}
catch // ... 异常处理
}
ThreadPool::Task ThreadPool::take()
{
MutexLockGuard lock(mutex_);
// always use a while-loop, due to spurious wakeup
while (queue_.empty() && running_)
{
notEmpty_.wait();
}
Task task;
if (!queue_.empty())
{
task = queue_.front();
queue_.pop_front();
if (maxQueueSize_ > 0)
{
notFull_.notify();
}
}
return task;
}
❓ 为什么线程池一般都使用定长数据结构来存储任务?