C++11线程池ThreadPool
功能描述
实现一种基于C++11的线程池ThreadPool,模仿Java的ThreadPoolExecutor;
使用示例
#include"ThreadPool.h"
#include<iostream>
#include<assert.h>
using namespace std;
using Task = std::function<void()>;
std::mutex mx;
void func() {
this_thread::sleep_for(std::chrono::milliseconds(20));
}
int main() {
ThreadPool<Task, ArrayBlockingQueue<Task*>> threadPool(5, 10, 500, std::unique_ptr<ArrayBlockingQueue<Task*>>(new ArrayBlockingQueue<Task*>(200)));
int success = 0;
int total = 300;
for (int i = 0; i < total; ++i) {
Task task = std::bind(func);
bool flag = threadPool.execute(task);
std::cout << (flag ? "success" : "failed") << std::endl;
success += flag;
std::cout << threadPool.workerCount() << std::endl;
}
std::cout << "success/total: " << success << "/" << total << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(5000));
std::cout << threadPool.workerCount() << std::endl;
}
关键函数说明
创建阻塞队列BlockingQueue
1)ArrayBlockingQueue实现了基于std::queue的有界阻塞队列;
2)参照该模板类的必要函数,用户可以根据阻塞队列类型不同,实现不同类型的阻塞队列(如:有界队列、无界队列、并发队列等);
3)为什么不写一个BlockingQueue抽象类供继承?
因为C++目前对于模板类中的模板函数不可以是虚函数;
ArrayBlockingQueue(int maxSize);
创建线程池ThreadPool
1)WorkQueuePtr为std::unique_ptr<BlockingQueue>类型;
2)其中BlockingQueue为阻塞队列,目前采用模板方式定义;
3)BlockingQueue放置的类型为Task*;
3)目前BlockingQueue仅仅实现了基于std::queue的有界阻塞队列;
//corePoolSize:核心线程数; 当corePoolSize=0时,只有queue中放不下时,才会创建非核心线程
//maximumPoolSize:最大线程数;
//keepAliveTime:活跃时间(单位毫秒);
//workQueuePtr:阻塞队列;
ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, WorkQueuePtr&& workQueuePtr);
提交任务
1)Task为执行的任务类型,为模板类型;
2)返回值表示是否成功添加到队列中或被线程执行;
//执行任务
//返回为false的可能情况:队列中满了,且线程数量已经达到最大值
bool execute(Task& task);
shutdown线程池
//关闭线程池,线程中当前的任务会执行完,但不会继续取任务队列中的任务
//是否需要支持暂停添加任务??
void shutdown();
设计思想
BlockingQueue设计
1)该类实例以组合的方式被ThreadPool持有,主要是为了让用户可以传入自定义的BlockingQueue,提高ThreadPool的扩展性;
2)BlockingQueue放置的类型为Task*;
execute执行流程
1)判断当前核心线程数是否达到极限corePoolSize,如果没有,则创建核心线程并负责执行该任务,并返回true;(执行完这个任务之后,会阻塞在queue上取任务,直到返回)
2)否则,尝试加入BlockingQueue中,成功,则返回true;
3)否则,判断当前线程数达到极限maximumPoolSize,如果没有,则创建非核心线程并负责执行该任务,并返回true;(执行完这个任务之后,会阻塞在queue上取任务,直到返回;当等待超时keepAliveTime后,该线程退出,并销毁对应资源)
4)否则,返回false,表示任务提交失败;
注:执行过程中会判断当前线程池的状态是否为Running,如果不是,也是直接返回false;
注:只有在核心线程数量达到极限corePoolSize且队列BlockingQueue中push失败,才会创建非核心线程;
ctl变量设计
1)ctl类型为atomic_uint32_t,存储线程池状态以及当前线程数量;
2)ctl前3位表示当前ThreadPool状态,后COUNT_BITS=29位表示当前线程数量;
3)如此设计,是为了在方便一起对线程状态以及线程数进行CAS,以避免无意义地持有mainLock;
BlockingQueue必须实现函数说明
1)为适配ThreadPool,以下为用户自定义的BlockingQueue需要实现的函数;
2)其中interrupt()唤醒等待在具有Predicate参数 的push或者pop函数的线程;
3)interrupt()设计目的是唤醒等待在queue上的线程,使得主线程可以通知线程退出;
const static int SUCCESS = 0; //成功
const static int TIMEOUT = 1; //超时
const static int INTERRUPT = 2; //中断;当传入的Predicate为真时,返回(被interrupt唤醒)
const static int RETRY = 3; //请重试
bool isEmpty();
bool isFull();
int size();
int push(Task& task);
int pop(Task& task);
template<typename Predicate>
int push(Task& task, Predicate pred);
template<typename Predicate>
int pop(Task& task, Predicate pred);
bool tryPush(Task& task);
bool tryPop(Task& task);
int push(Task& task, int timeout);
int pop(Task& task, int timeout);
template<typename Predicate>
int push(Task& task, int timeout, Predicate pred);
template<typename Predicate>
int pop(Task& task, int timeout, Predicate pred);
void interrupt(); //仅仅打断,仅影响wait的pred
Task类型实现要求
1)可以直接使用Task()的形式进行执行;
2)一种符合要求的Task如下;
using Task = std::function<void()>;
源码
BlockingQueue.h
#pragma once
#include<thread>
#include<queue>
#include<mutex>
#include<condition_variable>
//类型Task支持nullptr的赋值和相等操作
//采用queue+mutex实现有界队列
template<typename Task>
class ArrayBlockingQueue{
public:
const static int SUCCESS = 0; //成功
const static int TIMEOUT = 1; //超时
const static int INTERRUPT = 2; //中断;当传入的Predicate为真时,返回(被interrupt唤醒)
const static int RETRY = 3; //请重试
private:
std::queue<Task> queue;
int maxSize;
std::mutex mx;
std::condition_variable p_cond; //生产者cond
std::condition_variable c_cond; //消费者cond
ArrayBlockingQueue(const ArrayBlockingQueue&) = delete;
ArrayBlockingQueue& operator=</