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=(const ArrayBlockingQueue&) = delete;
public:
ArrayBlockingQueue(int maxSize):maxSize(maxSize){}
bool isEmpty() { return queue.size() == 0; }
bool isFull() { return queue.size() >= maxSize; }
int size() { return queue.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
};
template<typename Task>
int ArrayBlockingQueue<Task>::push(Task& task) {
std::unique_lock<std::mutex> lock(mx);
p_cond.wait(lock, [&]() {return !isFull(); });
queue.push(task);
c_cond.notify_one();
return SUCCESS;
}
template<typename Task>
int ArrayBlockingQueue<Task>::pop(Task& task) {
std::unique_lock<std::mutex> lock(mx);
c_cond.wait(lock, [&]() {return !isEmpty(); });
task = queue.front();
queue.pop();
p_cond.notify_one();
return SUCCESS;
}
template<typename Task>
template<typename Predicate>
int ArrayBlockingQueue<Task>::push(Task& task, Predicate pred) {
std::unique_lock<std::mutex> lock(mx);
p_cond.wait(lock, [&]() {return !isFull() || pred(); });
if (isFull() || pred()) return INTERRUPT; //被打断
queue.push(task);
c_cond.notify_one();
return SUCCESS;
}
template<typename Task>
template<typename Predicate>
int ArrayBlockingQueue<Task>::pop(Task& task, Predicate pred) {
std::unique_lock<std::mutex> lock(mx);
c_cond.wait(lock, [&]() {return !isEmpty() || pred(); });
if (isEmpty() || pred()) return INTERRUPT; //被打断
task = queue.front();
queue.pop();
p_cond.notify_one();
return SUCCESS;
}
template<typename Task>
bool ArrayBlockingQueue<Task>::tryPush(Task& task) {
std::unique_lock<std::mutex> lock(mx, std::try_to_lock_t());
if (lock.owns_lock() && !isFull()) {
queue.push(task);
c_cond.notify_one();
return SUCCESS;
}
return RETRY;
}
template<typename Task>
bool ArrayBlockingQueue<Task>::tryPop(Task& task) {
std::unique_lock<std::mutex> lock(mx, std::try_to_lock_t());
if (lock.owns_lock() && !isEmpty()) {
task = queue.front();
queue.pop();
p_cond.notify_one();
return SUCCESS;
}
return RETRY;
}
template<typename Task>
int ArrayBlockingQueue<Task>::push(Task& task, int timeout) {
std::unique_lock<std::mutex> lock(mx);
bool flag = p_cond.wait_for(lock, std::chrono::milliseconds(timeout), [&]() {return !isFull(); });
if (!flag) return TIMEOUT;
queue.push(task);
c_cond.notify_one();
return SUCCESS;
}
template<typename Task>
int ArrayBlockingQueue<Task>::pop(Task& task, int timeout) {
std::unique_lock<std::mutex> lock(mx);
bool flag = c_cond.wait_for(lock, std::chrono::milliseconds(timeout), [&]() {return !isEmpty(); });
if (!flag) return TIMEOUT;
task = queue.front();
queue.pop();
p_cond.notify_one();
return SUCCESS;
}
template<typename Task>
template<typename Predicate>
int ArrayBlockingQueue<Task>::push(Task& task, int timeout, Predicate pred) {
std::unique_lock<std::mutex> lock(mx);
bool flag = p_cond.wait_for(lock, std::chrono::milliseconds(timeout), [&]() {return !isFull() || pred(); });
if (!flag) return TIMEOUT;
if (isFull() || pred()) return INTERRUPT; //被打断
queue.push(task);
c_cond.notify_one();
return SUCCESS;
}
template<typename Task>
template<typename Predicate>
int ArrayBlockingQueue<Task>::pop(Task& task, int timeout, Predicate pred) {
std::unique_lock<std::mutex> lock(mx);
bool flag = c_cond.wait_for(lock, std::chrono::milliseconds(timeout), [&]() {return !isEmpty() || pred(); });
if (!flag) return TIMEOUT;
if (isEmpty() || pred()) return INTERRUPT; //被打断
task = queue.front();
queue.pop();
p_cond.notify_one();
return SUCCESS;
}
template<typename Task>
void ArrayBlockingQueue<Task>::interrupt() {
//需要加锁
//避免线程A在cond.wait()执行pred()后,线程B调用notify,然后线程A阻塞在cond上;这会使得线程A无法知道pred()条件已经被更新
std::unique_lock<std::mutex> lock(mx);
p_cond.notify_all();
c_cond.notify_all();
}
ThreadPool.h
#pragma once
#include<atomic>
#include<unordered_set>
#include<mutex>
#include<thread>
#include<condition_variable>
#include<functional>
#include<iostream>
#include"BlockingQueue.h"
//类型Task支持nullptr的赋值和相等操作
template<typename Task,typename BlockingQueue= ArrayBlockingQueue<Task*>>
class ThreadPool {
public:
//using Task = std::function<void()>;
//using BlockingQueue = BlockingQueue<Task>;
using WorkQueuePtr = std::unique_ptr<BlockingQueue>;
private:
//工作者
class Worker {
private:
ThreadPool* threadPoolPtr; //线程池句柄
Task* taskPtr; //任务
bool isCore; //是否为核心线程
int completeTask = 0; //已完成任务数
std::thread thread; //线程
friend class ThreadPool;
public:
Worker(ThreadPool* threadPoolPtr, Task* firstTask, bool isCore):
threadPoolPtr(threadPoolPtr), taskPtr(firstTask), isCore(isCore), thread(&Worker::work,this)
{
if (thread.joinable())
thread.detach();
}
~Worker() {}
bool isCoreThread() { return isCore; }
void work() {
threadPoolPtr->runWorker(this);
}
};
//尝试创建新线程
bool addWorker(Task* firstTask, bool isCore);
//worker调用
void runWorker(Worker* workerPtr);
void dieThreadHandle(Worker* workerPtr);
public:
//corePoolSize:核心线程数; 当corePoolSize=0时,只有queue中放不下时,才会创建非核心线程
//maximumPoolSize:最大线程数;
//keepAliveTime:活跃时间(单位毫秒);
//workQueuePtr:阻塞队列;
ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, WorkQueuePtr&& workQueuePtr);
~ThreadPool();
bool isRunning() { return isRunning(ctl); }
bool isShutDown() { return isShutDown(ctl); }
uint32_t workerCount() { return workerCount(ctl); }
//关闭线程池,线程中当前的任务会执行完,但不会继续取任务队列中的任务
//是否需要支持暂停添加任务??
void shutdown();
//执行任务
//返回为false的可能情况:队列中满了,且线程数量已经达到最大值
bool execute(Task& task);
private:
bool isRunning(uint32_t c) { return (c >> COUNT_BITS) == RUNNING; }
bool isShutDown(uint32_t c) { return (c >> COUNT_BITS) == SHUTDOWN; }
uint32_t workerCount(uint32_t c) { return c & CAPACITY; }
ThreadPool(const ThreadPool&) = delete;
ThreadPool& operator=(const ThreadPool&) = delete;
const static uint32_t INT32_COUNT = 32; //ctl位数
const static uint32_t COUNT_BITS = INT32_COUNT - 3; //前3位为状态码
const static uint32_t CAPACITY = (1 << COUNT_BITS) - 1; //最大线程数
//状态码
const static uint32_t RUNNING = 0;
const static uint32_t SHUTDOWN = 1;
const static uint32_t STOP = 2;
int corePoolSize; //核心线程数
int maximumPoolSize; //最大线程数
int keepAliveTime; //活跃时间(单位毫秒),作用:使得非核心线程在空闲时退出
WorkQueuePtr workQueuePtr; //工作队列
std::mutex mainLock;
std::condition_variable die_cond; //作用:通知线程退出(正常析构销毁对象)
std::unordered_set<Worker*>workerPtrs; //工作者
std::atomic_uint32_t ctl; //将状态和线程数放在一起,方便CAS;当前状态(3位)+当前线程数
};
//尝试创建新线程
template<typename Task, typename BlockingQueue>
bool ThreadPool<Task, BlockingQueue>::addWorker(Task* firstTask, bool isCore) {
while (true) {
auto c = ctl.load();
if (!isRunning(c)) return false;
int wc = workerCount(c);
if (wc >= (isCore ? corePoolSize : maximumPoolSize))
return false;
if (ctl.compare_exchange_strong(c, c + 1))//CAS
break;
}
std::lock_guard<std::mutex> lock(mainLock);
if (!isRunning()) {
--ctl;
die_cond.notify_all(); //使得threadpool正常析构
return false;
}
Worker* workerPtr = new Worker(this, firstTask, isCore);
workerPtrs.insert(workerPtr);
return true;
}
//worker调用
template<typename Task, typename BlockingQueue>
void ThreadPool<Task, BlockingQueue>::runWorker(Worker* workerPtr) {
if (workerPtr == nullptr) return;
if (workerPtr->taskPtr != nullptr) {
(*workerPtr->taskPtr)();
++workerPtr->completeTask;
delete workerPtr->taskPtr;
workerPtr->taskPtr = nullptr;
}
int flag = 0;
while (isRunning()) {
if (!workerPtr->isCoreThread()) {
flag = workQueuePtr->pop(workerPtr->taskPtr, keepAliveTime, [&]() {return !isRunning(); });
if (flag == BlockingQueue::TIMEOUT)
break;
}
else
flag = workQueuePtr->pop(workerPtr->taskPtr, [&]() {return !isRunning(); });
if (flag == BlockingQueue::SUCCESS && workerPtr->taskPtr!=nullptr) {
(*workerPtr->taskPtr)();
++workerPtr->completeTask;
}
if (workerPtr->taskPtr != nullptr) {
delete workerPtr->taskPtr;
workerPtr->taskPtr = nullptr;
}
}
dieThreadHandle(workerPtr);
}
template<typename Task, typename BlockingQueue>
void ThreadPool<Task, BlockingQueue>::dieThreadHandle(Worker* workerPtr) {
std::lock_guard<std::mutex> lock(mainLock);
workerPtrs.erase(workerPtr);
--ctl;
delete workerPtr;
die_cond.notify_all();
}
//corePoolSize:核心线程;
//maximumPoolSize:最大线程;
//keepAliveTime:活跃时间(单位毫秒);
//workQueuePtr:阻塞队列;
template<typename Task, typename BlockingQueue>
ThreadPool<Task, BlockingQueue>::ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, WorkQueuePtr&& workQueuePtr) :
corePoolSize(corePoolSize), maximumPoolSize(maximumPoolSize),
keepAliveTime(keepAliveTime), workQueuePtr(std::move(workQueuePtr)), ctl(0)
{
}
template<typename Task, typename BlockingQueue>
ThreadPool<Task, BlockingQueue>::~ThreadPool() {
shutdown();
std::unique_lock<std::mutex> lock(mainLock);
die_cond.wait(lock, [&]() {return workerCount() == 0; });
Task* taskPtr = nullptr;
while (!workQueuePtr->isEmpty()) {
workQueuePtr->tryPop(taskPtr);
if (taskPtr != nullptr) {
delete taskPtr;
taskPtr = nullptr;
}
}
}
template<typename Task, typename BlockingQueue>
void ThreadPool<Task, BlockingQueue>::shutdown() {
if (isShutDown()) return;
//CAS
//在特殊情况下,在设置shutdown后,依然有些任务可以正常加入queue中
while (true) {
auto expected = ctl.load();
auto desired = (SHUTDOWN << COUNT_BITS) | (expected & CAPACITY);
if (ctl.compare_exchange_strong(expected, desired)) //将状态设置为SHUTDOWN
break;
}
//唤醒等待在queue上的队列
workQueuePtr->interrupt();
}
template<typename Task, typename BlockingQueue>
bool ThreadPool<Task, BlockingQueue>::execute(Task& task) {
Task* taskPtr = new Task(task);
auto c = ctl.load();
auto wc = workerCount(c);
//当前线程小于corePoolSize,并且成功放入workQueuePtr;
if (wc < corePoolSize) {
if (addWorker(taskPtr, true)) return true;
//此时,ctl变化
c = ctl.load();
}
//当前处于运行状态,且成功加入队列
if (isRunning(c) && workQueuePtr->tryPush(taskPtr) == BlockingQueue::SUCCESS) {
return true;
}
//尝试开新线程
return addWorker(taskPtr, false);
}