由于原生c++中并未给我们提供线程池类,所有我想基于c++11语法封装一个属于自己的c++线程池类,方便自己的练习项目使用。
CppWinks的gitee链接:cppwinks: 基于C++11的CW库 (gitee.com)
先来简单介绍一下我自己封装的CW库的优点:
- 简单易用:CW库封装的类和API函数十分简洁并且注释完整,命名规范遵循谷歌命名规范,使得开发者能够轻松地了解如何使用库。
- 跨平台性:CW库能够在不同的操作系统和编译器上运行,方便开发者在不同平台上开发和发布他们的应用程序。
- 高效性:CW库中的类和API函数运行尽可能地高效,以减少对开发者时间和资源的消耗。
- 稳定性:CW库运行较为稳定,不出现频繁的更新和bug。
- 可扩展性:CW库完全开源免费,能够方便地扩展和修改,以适应不同的需求和场景。
- 安全性:CW库提供了足够的安全性,以防止潜在的安全漏洞。
- 兼容性:CW库与其他流行的C++库和框架相互兼容,方便开发者快速集成和使用。
目前CW库已支持的类和API:
- C++11线程池
- C++11Linux下的TCP/IP网络编程
- C++11对Http请求和回复的解析
下面对CWThreadPool的实现进行简单介绍:
工作原理图:
使用到的部分C++类:
- std::string
- std::thread
- std::vector
- std::queue
- std::unordered_map
- std::shared_ptr
- std::make_shared
- std::thread
- std::condition_variable
- std::mutex
- std::unique_lock
实现原理简介:
- 构造函数初始化线程池:传入最小最大工作线程数和管理者线程轮询频率来初始化线程池的部分成员,分别为管理者线程和工作线程分配内存并开始工作。
- 管理者线程工作函数:需要静态成员函数,非静态成员函数无法完成std::thread中第二个参数的bind过程。管理者线程需要每隔固定时间轮询一次,每次需要先查看当前线程池的关闭标志位,如果是true则要join所有工作线程后退出管理者线程循环,保证析构函数中可以对管理者线程完成join。如果不为true,则会查看当前线程池中繁忙线程个数和存活线程个数,基于管理算法进行动态的调整存活线程个数,使其在线程池规定的最小最大存活线程个数之间,并尽可能少的占用资源同时又能保留部分存活休眠线程保证突然增加任务时无法及时进行新的线程创建。
- 工作线程工作函数:同样需要静态成员函数,非静态成员函数无法完成std::thread中第二个参数的bind过程。工作线程需要不断轮询查看任务队列中是否存在任务函数,如果有则取出并执行,如果没有则通过条件变量进入阻塞睡眠状态,等待添加任务时进行唤醒。
- 析构函数销毁线程池:当线程池的析构函数被调用时,会将线程池是否销毁的标志位置为true,随后直接阻塞等待管理者线程释放,这一过程由于管理者线程在释放前会先释放所有工作线程,所以无序在析构函数中对工作线程进行阻塞释放。
关于生产者消费者模型:
根据原理图可以看出最大的生产者就是任务队列,而最大的消费者是工作线程,其实还有一对隐含的生产者消费者,就是退出线程的数组和管理者线程,在最初的设计中由于需要设计退出线程,但是又无法在管理者线程中找到哪一个线程已经结束可以进行join并释放堆内存,所以便将保存工作线程的容器数据结构修改为了unordered_map,键表示其线程id,而退出线程的数组用来保存退出的线程的id号,这样每次管理者线程轮询时就可以根据退出线程数组中保存的id在unordered_map中查找并join释放即可。
下面是CW库中的线程池完整源代码:
cwthreadpool.h
#pragma once
#include <iostream>
#include <thread>
#include <vector>
#include <queue>
#include <unordered_map>
#include <mutex>
#include <unistd.h>
#include <condition_variable>
// 重命名回调函数
typedef void (*callback)(void *arg);
// 定义任务结构体
struct CWTask
{
public:
// 任务构造函数
CWTask(callback func_, void *arg_) : func(func_), arg(arg_) {}
callback func;
void *arg;
};
class CWThreadPool
{
private:
// 最小存活线程数
const int kMin_liveNumber;
// 最大存活线程数
const int kMax_liveNumber;
// 管理者线程每次轮询时间间隔
const unsigned int kManager_thread_interval;
// 退出线程数
int exitNumber = 0;
// 繁忙线程数
int busyNumber = 0;
// 线程池是否关闭
bool is_shutdown = false;
// 线程池互斥锁
std::mutex threadPool_mutex;
// 控制工作线程是否阻塞等待
std::condition_variable worker_threads_cv;
// 管理者线程
std::shared_ptr<std::thread> manager_thread;
// 工作线程
std::unordered_map<std::thread::id, std::shared_ptr<std::thread>> worker_threads;
// 需要退出的线程ID
std::vector<std::thread::id> exit_threadIDs;
// 任务队列
std::queue<CWTask> task_queue;
// 工作线程工作函数
static void worker(CWThreadPool *thread_pool);
// 管理者线程工作函数
static void manager(CWThreadPool *thread_pool);
public:
// 构造函数,传入最小存活线程和最大存活线程数
CWThreadPool(const int min, const int max, const unsigned int interval);
// 析构函数,阻塞释放线程池中的各种资源
~CWThreadPool();
// 为线程池添加任务
void addTask(CWTask task);
// 添加任务函数重载
void addTask(callback func, void *arg);
};
cwthreadpool.cpp
#include "cwthreadpool.h"
// 构造函数,传入最小存活线程和最大存活线程数
CWThreadPool::CWThreadPool(const int min, const int max, const unsigned int interval)
: kMin_liveNumber(min), kMax_liveNumber(max), kManager_thread_interval(interval)
{
// 创建管理者线程
manager_thread = std::make_shared<std::thread>(manager, this);
// 创建工作线程并插入到工作线程哈希表中
for (int i = 0; i < kMin_liveNumber; ++i)
{
const auto ptr = std::make_shared<std::thread>(worker, this);
worker_threads.emplace(ptr->get_id(), ptr);
}
}
// 析构函数,阻塞释放线程池中的各种资源
CWThreadPool::~CWThreadPool()
{
// 设置线程池退出标志为true并阻塞等待管理者线程销毁
is_shutdown = true;
manager_thread->join();
}
// 为线程池添加任务
void CWThreadPool::addTask(CWTask task)
{
threadPool_mutex.lock();
task_queue.push(task);
threadPool_mutex.unlock();
worker_threads_cv.notify_one();
}
// 添加任务函数重载
void CWThreadPool::addTask(callback func, void *arg)
{
threadPool_mutex.lock();
task_queue.push(CWTask(func, arg));
threadPool_mutex.unlock();
worker_threads_cv.notify_one();
}
// 工作线程工作函数
void CWThreadPool::worker(CWThreadPool *thread_pool)
{
while (true)
{
// 模板类锁管理线程池互斥锁的加/解锁
std::unique_lock<std::mutex> lk(thread_pool->threadPool_mutex);
// 如果任务队列是空就阻塞等待
if (thread_pool->task_queue.empty())
{
thread_pool->worker_threads_cv.wait(lk);
}
// 如果线程池关闭则直接结束该线程
if (thread_pool->is_shutdown)
{
// 解锁防止死锁
lk.unlock();
break;
}
// 判断是否有需要退出的线程
else if (thread_pool->exitNumber > 0)
{
// 将自己的线程ID插入退出线程数组中
thread_pool->exit_threadIDs.push_back(std::this_thread::get_id());
// 消费一个退出线程的计数器
--thread_pool->exitNumber;
// 解锁防止死锁
lk.unlock();
break;
}
// 取任务队列一个任务
CWTask task = thread_pool->task_queue.front();
thread_pool->task_queue.pop();
// 增加线程池繁忙线程个数
++thread_pool->busyNumber;
std::cout << "线程:" << std::this_thread::get_id() << "开始工作..." << std::endl;
// 解锁让其他工作线程可以取任务
lk.unlock();
// 执行任务的回调函数
task.func(task.arg);
std::cout << "线程:" << std::this_thread::get_id() << "完成工作..." << std::endl;
lk.lock();
// 减少线程池繁忙线程个数
--thread_pool->busyNumber;
lk.unlock();
}
}
// 管理者线程
void CWThreadPool::manager(CWThreadPool *thread_pool)
{
while (true)
{
thread_pool->threadPool_mutex.lock();
std::cout << "存活线程:" << thread_pool->worker_threads.size() << " 繁忙线程:" << thread_pool->busyNumber << std::endl;
// 检查线程池是否要关闭
if (thread_pool->is_shutdown)
{
// 唤醒所有休眠阻塞的工作线程使它们自杀
thread_pool->worker_threads_cv.notify_all();
// 阻塞等待所有工作线程结束
for (auto &it : thread_pool->worker_threads)
{
it.second->join();
}
// 释放工作线程中的所有堆区资源
thread_pool->worker_threads.clear();
// 解锁防止死锁并结束循环
thread_pool->threadPool_mutex.unlock();
break;
}
// 释放要退出的工作线程
if (thread_pool->exit_threadIDs.size() > 0)
{
for (const auto &it : thread_pool->exit_threadIDs)
{
auto exit_thread_it = thread_pool->worker_threads.find(it);
// 如果工作线程中存在该线程
if (exit_thread_it != thread_pool->worker_threads.end())
{
// 阻塞等待线程结束
exit_thread_it->second->join();
// 释放该线程在堆区的内存
thread_pool->worker_threads.erase(exit_thread_it);
std::cout << "线程:" << it << "已释放..." << std::endl;
}
}
thread_pool->exit_threadIDs.clear();
}
// 判断线程池需要增加/杀死存活线程
if (thread_pool->busyNumber >= thread_pool->worker_threads.size() - 1 && thread_pool->worker_threads.size() < thread_pool->kMax_liveNumber)
{
std::cout << "线程池压力较大,增加存活线程缓解压力..." << std::endl;
// 计算需要扩容后的存活线程个数
int thread_count = thread_pool->busyNumber * 2 - 1;
// 防止扩容后存活线程数溢出或增加失效
if (thread_count > thread_pool->kMax_liveNumber)
{
thread_count = thread_pool->kMax_liveNumber;
}
else if (thread_count <= 1)
{
thread_count = 2;
}
// 增加工作线程
for (int i = 0; i < thread_count - thread_pool->worker_threads.size(); ++i)
{
auto ptr = std::make_shared<std::thread>(worker, thread_pool);
thread_pool->worker_threads.emplace(ptr->get_id(), ptr);
}
}
else if (thread_pool->busyNumber <= thread_pool->worker_threads.size() * 2 / 3 && thread_pool->worker_threads.size() > thread_pool->kMin_liveNumber)
{
std::cout << "线程池压力较小,减少存活线程..." << std::endl;
// 减少后存活线程的个数
int thread_count = thread_pool->worker_threads.size() * 5 / 6;
// 防止减少后少于线程池最少存活线程
if (thread_count < thread_pool->kMin_liveNumber)
{
thread_count = thread_pool->kMin_liveNumber;
}
// 设置退出线程个数
thread_pool->exitNumber = thread_pool->worker_threads.size() - thread_count;
// 通过条件变量唤醒正在休眠的工作线程,使其自杀
for (int i = 0; i < thread_pool->exitNumber; ++i)
{
thread_pool->worker_threads_cv.notify_one();
}
}
// 解锁防止死锁
thread_pool->threadPool_mutex.unlock();
// 管理者线程休眠固定周期
usleep(thread_pool->kManager_thread_interval);
}
}