原理:
采用c++11中的thread.h,mutex.h编写线程池类
原理是在类对象中设计管理者线程函数与工作者线程函数,其中管理者只需要一名即可,工作者数量可人为设置上下限,具体数量需要在运行中由管理者根据要处理的任务数量决定。
每当有任务出现时,会将其载入任务缓冲区队列中,工作者们会自动从任务队列中取走任务并进行处理,且正在处理任务的工作者会被标记为忙碌状态。如果任务队列为空时,本来要去取任务的工作者便会进入阻塞状态,即停止运行直到有新的任务进入任务队列中。
当任务量增加时,如果任务量已经超过工作者数量,管理者会自动添加一定数量的工作者。
当任务量减少时,如果忙碌的工作者数量远远少于工作者的总数,管理者便会消除一定数量的工作者。
其中,管理者并非一直运行的,因为管理者会操纵大量的共享变量,一直运行会不停的为共享变量上锁使效率变低,故需要人工设置管理者监察时间间隔。
头文件如下:
#pragma once
#include <thread>
#include <mutex>
#include <queue>
#include <iostream>
#define CHANGE_NUM 3
using namespace std;
typedef struct Task {
void* (*pfunc)(void* arg);
void* arg;
}Task;
typedef struct Thread {
bool alive;//记录工作者数组中哪个位置有工作者
thread* mythread;
}Thread;
class ThreadPool {
private:
int maxNum;//最大工作者数量
int minNum;//最小工作者数量
int newThreads;//用于记录总共新建了多少工作者
int destoryThreads;//用于记录总共销毁了多少工作者
atomic_int destoryNum;//需要销毁的工作者数量
atomic_int aliveNum;//存在的工作者总数
atomic_int busyNum;//进入忙碌状态的工作者数量
atomic_bool shutdown;//关闭线程池标志
queue<Task> taskQ;//任务缓冲区队列
queue<thread*> threads;//用于存放建立的工作者线程,便于在析构函数中逐个join
thread* manager;//管理者线程
Thread* workers;//工作者线程组
mutex myLock;//锁
condition_variable isEmpty;//条件变量,用于在任务队列为空时对工作者进行阻塞
public:
ThreadPool(const int& maxThread, const int& minThread);
~ThreadPool();
static void managerThread(ThreadPool* pool);
static void workerThread(ThreadPool* pool, int site);
void addTask(void* (*threadFunc)(void*), void* arg);
int getAliveNum();
int getBusyNum();
int getTaskQsize();
};
cpp文件如下:
#include "ThreadPool.h"
ThreadPool::ThreadPool(const int& maxNum, const int& minNum)
:maxNum(maxNum), minNum(minNum)
{
this->shutdown = false;
this->aliveNum = 0;
this->busyNum = 0;
this->destoryNum = 0;
this->newThreads = 0;
this->destoryThreads = 0;
this->workers = new Thread[maxNum];
for (int i = 0; i < maxNum; i++) {
this->workers[i].alive = false;
this->workers[i].mythread = nullptr;
}
this->manager = new thread(managerThread, this);//创建管理者
for (int i = 0; i < minNum; i++) {//先创建下限数量的工作者
this->aliveNum++;
this->myLock.lock();
this->workers[i].alive = true;//数组中哪个位置有工作者就把哪个位置标记为true
this->workers[i].mythread = new thread(workerThread, this, i);
this->threads.push(this->workers[i].mythread);
this->myLock.unlock();
}
}
ThreadPool::~ThreadPool() {
this->shutdown = true;
this->manager->join();
delete this->manager;
cout << "\n最大线程数:" << this->newThreads + this->minNum << endl;
cout << "残余任务量:" << this->taskQ.size() << endl;
cout << "总计新建线程数:" << this->newThreads << endl;
cout << "总计销毁线程数:" << this->destoryThreads << "\n" << endl;
this->isEmpty.notify_all();
while (!this->threads.empty()) {
this->threads.front()->join();//对每一个创建过的工作者join,不然报错
this->threads.pop();
}
}
void ThreadPool::managerThread(ThreadPool* pool) {
do {
pool->myLock.lock();
//如果任务数量大于存活线程数量且当前线程数未超过最大线程值则增加线程
if (pool->taskQ.size() > pool->aliveNum && pool->aliveNum < pool->maxNum) {
int count = CHANGE_NUM;//最多一次性增加三个线程
pool->destoryNum = 0;//需要新建工作者时先清零销毁数量,否则刚新建的工作者就被销毁了
for (int i = 0; i < pool->maxNum && count != 0 && pool->aliveNum < pool->maxNum; i++) {
if (pool->workers[i].alive == false) {
count--;
pool->aliveNum++;
pool->newThreads++;
pool->workers[i].alive = true;
pool->workers[i].mythread = new thread(workerThread, pool, i);
pool->threads.push(pool->workers[i].mythread);
cout << "创建新的线程,线程ID为:" << pool->workers[i].mythread->get_id() << endl;
}
}
}
pool->myLock.unlock();
//如果忙碌线程的二倍仍小于忙碌线程数量且存活线程数量未低于最小线程值则销毁线程
if (pool->busyNum * 2 < pool->aliveNum && pool->aliveNum > pool->minNum) {
pool->destoryNum = CHANGE_NUM;
for (int i = 0; i < 3; i++)
pool->isEmpty.notify_one();
}
this_thread::sleep_for(1500ms);//管理者每1.5s监管一次
} while (!pool->shutdown);
}
void ThreadPool::workerThread(ThreadPool* pool, int site) {
do {
unique_lock<mutex> guard(pool->myLock);
while (pool->taskQ.empty()) {//若任务队列为空则进入条件变量阻塞
cout << "线程 " << this_thread::get_id() << " 陷入阻塞" << endl;
pool->isEmpty.wait(guard);
cout << "线程 " << this_thread::get_id() << " 解除阻塞" << endl;
if (pool->shutdown) {
pool->aliveNum--;
pool->workers[site].alive = false;
guard.unlock();
cout << "关闭" << this_thread::get_id() << "线程" << endl;
return;
}
}
if (pool->destoryNum && pool->aliveNum > pool->minNum) {//阻塞结束后判断是否需要销毁当前线程
pool->destoryThreads++;
pool->destoryNum--;
pool->aliveNum--;
pool->workers[site].alive = false;
guard.unlock();
cout << "销毁" << this_thread::get_id() << "线程" << endl;
return;
}
cout << "第 " << *(int*)pool->taskQ.front().arg << " 个任务被取出" << endl;
Task task = pool->taskQ.front();//从任务队列中获取一个任务
pool->taskQ.pop();//将该任务从队列中剔除
guard.unlock();
pool->busyNum++;
task.pfunc(task.arg);//执行任务
pool->busyNum--;
} while (!pool->shutdown);
cout << "关闭" << this_thread::get_id() << "线程" << endl;
}
void ThreadPool::addTask(void* (*threadFunc)(void*), void* arg) {
Task task;
task.pfunc = threadFunc;
task.arg = arg;
this->myLock.lock();
cout << "第 " << *(int*)arg << " 个任务入队" << endl;
this->taskQ.push(task);
this->isEmpty.notify_one();
this->myLock.unlock();
}
int ThreadPool::getAliveNum() {
return this->aliveNum;
}
int ThreadPool::getBusyNum() {
return this->busyNum;
}
int ThreadPool::getTaskQsize() {
this->myLock.lock();
int num = this->taskQ.size();
this->myLock.unlock();
return num;
}
测试环节:
模拟输入500个任务,每个任务需要处理0~500ms,其中当发送第100~400个任务时迎来高峰期,即任务量增大。
测试代码及结果如下:
#include "ThreadPool.h"
#include <time.h>
#include <stdlib.h>
#include <chrono>
void* taskFunc(void* arg) {
this_thread::sleep_for(chrono::milliseconds(rand() % 600));
int num = *(int*)arg;
cout << "第 " << num << " 个任务已经完成" << endl;
return nullptr;
}
int main() {
srand((int)time(0));
ThreadPool pool(25, 5);
cout << "主线程启动:" << this_thread::get_id() << endl;
clock_t start, end, start_, end_;
double interval = 0;
start = clock();
for (int i = 0; i < 500; i++) {
int num = i;
pool.addTask(taskFunc, &num);
if (i < 100 || i > 400)
this_thread::sleep_for(chrono::milliseconds(rand() % 300));
else {
start_ = clock();
this_thread::sleep_for(chrono::milliseconds(rand() % 30));
end_ = clock();
interval += double(end_ - start_) / CLOCKS_PER_SEC;
}
cout << "当前线程数量:" << pool.getAliveNum() << ";忙碌线程数量:" << pool.getBusyNum() << ";任务队列大小:" << pool.getTaskQsize() << endl;
}
cout << "\n*******主线程结束!******\n" << endl;
end = clock();
cout << "\n处理全部任务花费时间: " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
cout << "\n处理200 ~ 300个任务花费时间: " << interval << "s" << endl;
return 0;
}
可见,在任务高峰时,线程池会自动创建大量工作者来应对,只需要6.626s便处理全部任务,并且高峰结束时,会自动销毁掉不必要存在的工作者。