前言
要向高效的实现对逻辑事物的处理,线程池是一个必不可少的东西,根据陈硕老师的思想:线程池作为一种宝贵的资源,数量应适宜,并且固定。Linux下创建线程唯一的一个函数就是pthread_create,创建的同时必须指定一个函数指针以及传入的参数,执行完毕就使用pthread_join函数回收,分配的栈空间被回收,下次只能是一个新的线程,要想不被回收,就只能一直运行这个函数,使用一个容器来将所有的请求存储起来,让这些个线程从容器里取这个请求,一直运行,当然也可以关闭这些线程。那么下面将结合muduo的源码简单分析一下:
源码
头文件threadpool.h
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
//
// Author: Shuo Chen (chenshuo at chenshuo dot com)
#ifndef MUDUO_BASE_THREADPOOL_H
#define MUDUO_BASE_THREADPOOL_H
#include "muduo/base/Condition.h"
#include "muduo/base/Mutex.h"
#include "muduo/base/Thread.h"
#include "muduo/base/Types.h"
#include <deque>
#include <vector>
namespace muduo
{
class ThreadPool : noncopyable
{
public:
typedef std::function<void ()> Task;
explicit ThreadPool(const string& nameArg = string("ThreadPool"));
~ThreadPool();
// Must be called before start().
void setMaxQueueSize(int maxSize) { maxQueueSize_ = maxSize; }
void setThreadInitCallback(const Task& cb)
{ threadInitCallback_ = cb; }
void start(int numThreads);
void stop();
const string& name() const
{ return name_; }
size_t queueSize() const;
// Could block if maxQueueSize > 0
// Call after stop() will return immediately.
// There is no move-only version of std::function in C++ as of C++14.
// So we don't need to overload a const& and an && versions
// as we do in (Bounded)BlockingQueue.
// https://stackoverflow.com/a/25408989
void run(Task f);
private:
bool isFull() const REQUIRES(mutex_);
void runInThread();
Task take();
mutable MutexLock mutex_;
Condition notEmpty_ GUARDED_BY(mutex_);
Condition notFull_ GUARDED_BY(mutex_);
string name_;
Task threadInitCallback_;
//采用uinque_ptr,不允许对线程指针的拷贝
std::vector<std::unique_ptr<muduo::Thread>> threads_;
std::deque<Task> queue_ GUARDED_BY(mutex_);
size_t maxQueueSize_;
bool running_;
};
} // namespace muduo
#endif // MUDUO_BASE_THREADPOOL_H
threadpool.c
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
//
// Author: Shuo Chen (chenshuo at chenshuo dot com)
#include "muduo/base/ThreadPool.h"
#include "muduo/base/Exception.h"
#include <assert.h>
#include <stdio.h>
using namespace muduo;
ThreadPool::ThreadPool(const string& nameArg)
: mutex_(),
notEmpty_(mutex_),
notFull_(mutex_),
name_(nameArg),
maxQueueSize_(0),
running_(false)
{
}
构造函数,完成数据的初始化,包括一个互斥锁,两个条件变量,线程池不可能没有上限,因此需要两个条件变量判断池中的上界以及下界
ThreadPool::~ThreadPool()
{
if (running_)
{
stop();
}
}
析构函数,将所有的线程全部关闭
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_();
}
}
线程池的启动函数,逐个调用Thread::start函数来创建和启动线程,其实根据线程的创建即启动原则,执行start函数才算是真正创建。Thead类中默认接收的函数对象是std::function<void()>类型,没有参数,而runInThread是一个成员函数,必须指定一个该类对象指针,因此将其和this绑定,threadInitCallback_函数名也很直观,线程池为0时并指定了这个回调函数就调用。
void ThreadPool::stop()
{
{
MutexLockGuard lock(mutex_);
running_ = false;
notEmpty_.notifyAll();
notFull_.notifyAll();
}
for (auto& thr : threads_)
{
thr->join();
}
}
该成员函数终止所有线程,因此需要把其中的running_变量设置为fasle,并给他们发送信号,信号发送完毕,就进行解锁,调用线程回收函数。
size_t ThreadPool::queueSize() const
{
MutexLockGuard lock(mutex_);
return queue_.size();
}
获取任务队列的大小
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();
}
}
见注释
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();
}
}
//此刻如果停止的话,返回的task就是一个空的
return task;
}
从任务队列中取任务, 任务队列为空则等待,直到任务队列不为空,则弹出一个,此时肯定不满,通知一下可以继续放。
bool ThreadPool::isFull() const
{
mutex_.assertLocked();
return maxQueueSize_ > 0 && queue_.size() >= maxQueueSize_;
}
//和之前写的一个样子,创建即运行,运行则不停止
void ThreadPool::runInThread()
{
try
{
if (threadInitCallback_)
{
threadInitCallback_();
}
while (running_)
{
//不断从任务队列中取函数对象执行
Task task(take());
if (task)
{
task();
}
}
}
catch (const Exception& ex)
{
fprintf(stderr, "exception caught in ThreadPool %s\n", name_.c_str());
fprintf(stderr, "reason: %s\n", ex.what());
fprintf(stderr, "stack trace: %s\n", ex.stackTrace());
abort();
}
catch (const std::exception& ex)
{
fprintf(stderr, "exception caught in ThreadPool %s\n", name_.c_str());
fprintf(stderr, "reason: %s\n", ex.what());
abort();
}
catch (...)
{
fprintf(stderr, "unknown exception caught in ThreadPool %s\n", name_.c_str());
throw; // rethrow
}
}