线程池
线程池的概念在这里就不一一赘述,本文主要实现的线程池是基于pthread库,然后开发环境的windows平台,IDE VS2015。本文主要实现的功能就是实现一个线程池,线程池的大小可以用户自定义,线程处理函数也可以用户自定义。操作简单,使用方便,目前只是初次编写,如果有问题欢迎大家提出。至于pthread在window的配置和普通的添加include lib dll文件一样。
结构图
整个流程大概如此,在处理事件中存放的ThreadTask*整个基类的指针,只要添加任务的时候继承于基类,并重写doTask()方法既可以完成相应的自己添加的线程函数。
线程池声明的头文件ThreadPool Class
#pragma once
#define HAVE_STRUCT_TIMESPEC
#include <stdio.h>
#include <pthread.h>
#include <assert.h>
#include <vector>
#include"ThreadTask.h"
#pragma comment(lib, "pthreadVC2.lib")
#define CIRCLETIME 1000
typedef long long llong;
class ThreadPool
{
public:
//构造函数
/*
*/
ThreadPool(int minThreadPool = 10, int maxThreadPool = 100, int queueSize = 100);
//用户自己选择是否要加入管理者线程。
/*管理者线程,自动管理线程池,当线程的繁忙数量/存在数量>0.8(默认)时开始增加线程,
*当小于0.3时,开始减少线程,每次增加较少的为繁忙线程的0.25或者较少存在线程的0.25
*这样可以使得线程池的工作量始终保持在0.4-0.6666之间,用户也可以改变默认参数
*para: maxThreshold, 增加线程的阈值
*para: minThreshodl, 减少线程的阈值
*para: weight, 增加或者减少的比重
*/
void addAdjustThread(float maxThreshold=0.8,float minThreshodl=0.3,float weight=0.25);
//朝线程池中添加任务
void addTask(ThreadTask*);
//访问线程一些资源的方法
inline int getLiveNum() const {
return _liveThreadNum;
}
inline int getBusyNum() const {
return _busyThreadNum;
}
virtual ~ThreadPool();
private:
//线程调用函数
static void *threadpoolTask(void *threadpool);
//管理者线程方法
static void *adjustThread(void *threadpool);
//判断线程是否活着
bool isAlive(pthread_t tid);
//释放线程池资源
void threadPoolFree();
//销毁线程池
bool threadPoolDestroy();
private:
int _minimumPoolSize; //默认线程池的大小
int _maximumPoolSize; //线程池最大线程个数
int _liveThreadNum; //存活的线程个数
int _busyThreadNum; //忙状态的线程个数
int _waitExitThreadNum; //需要销毁的线程个数
//锁
pthread_mutex_t _lock; //锁住整个class
pthread_mutex_t _threadCounter; //锁住用来记录忙线程的个数
pthread_cond_t _queueNotFull; /* 当任务队列满时,添加任务的线程阻塞,等待此条件变量 */
pthread_cond_t _queueNotEmpty; /* 任务队列里不为空时,通知等待任务的线程 */
pthread_t *_threads; /* 存放线程池中每个线程的tid。数组 */
pthread_t _adjustTid; /* 存管理线程tid */
std::vector<ThreadTask*>* _taskQueue; /* 任务队列 ,用vector模拟循环队列*/
int _queueFront; /* task_queue队头下标 */
int _queueRear; /* task_queue队尾下标 */
int _queueSize; /* task_queue队中实际任务数 */
int _queueMaxSize; /* task_queue队列可容纳任务数上限 */
bool _shutdown; /* 标志位,线程池使用状态,true或false */
float _maxThreshold;
float _minThreshold;
float _weight;
};
线程池定义的Cpp文件
#include "ThreadPool.h"
#include<iostream>
#include<Windows.h>
ThreadPool::ThreadPool(int minThreadPool, int maxThreadPool, int queueSize) :
_minimumPoolSize(minThreadPool),
_maximumPoolSize(maxThreadPool),
_liveThreadNum(minThreadPool),
_busyThreadNum(0),
_waitExitThreadNum(0),
_lock(NULL),
_threadCounter(NULL),
_queueNotFull(NULL),
_queueNotEmpty(NULL),
_threads(NULL),
_taskQueue(NULL),
_queueFront(0),
_queueRear(0),
_queueSize(0),
_queueMaxSize(queueSize),
_shutdown(false),
_maxThreshold(0.0),
_minThreshold(0.0),
_weight(0.0)
{
do {
_threads = new pthread_t[_maximumPoolSize];//创建线程池
if (nullptr == _threads) {
std::cout << "bad_alloc" << std::endl;
break;
}
//初始化锁
if (pthread_mutex_init(&_lock, NULL) != 0
|| pthread_mutex_init(&_threadCounter, NULL) != 0
|| pthread_cond_init(&_queueNotFull, NULL) != 0
|| pthread_cond_init(&_queueNotEmpty, NULL) != 0)
{
std::cout << "init lock or cond fail" << std::endl;
break;
}
_taskQueue = new std::vector<ThreadTask*>(queueSize, nullptr);
//启动最小的工作线程
//using cel = ThreadPool::*p;
for (int i = 0; i < _minimumPoolSize; ++i) {
pthread_create(&_threads[i], NULL, &ThreadPool::threadpoolTask, this);
}
//启动管理者线程
return;
} while (0);
threadPoolFree();
return;
}
void ThreadPool::addAdjustThread(float maxThreshold, float minThreshold, float weight)
{
_maxThreshold = maxThreshold;
_minThreshold = minThreshold;
_weight = weight;
pthread_create(&this->_adjustTid, NULL, &ThreadPool::adjustThread, this);
}
void ThreadPool::threadPoolFree()
{
if (_threads != nullptr) {
delete[]_threads;
pthread_mutex_lock(&(_lock));
pthread_mutex_destroy(&(_lock));
pthread_mutex_lock(&(_threadCounter));
pthread_mutex_destroy(&(_threadCounter));
pthread_cond_destroy(&(_queueNotEmpty));
pthread_cond_destroy(&(_queueNotFull));
}
if (_taskQueue != nullptr) {
delete _taskQueue;
}
}
bool ThreadPool::threadPoolDestroy()
{
if (nullptr == this) {
return false;
}
_shutdown = true;
//关闭管理者线程
if (_adjustTid.x == 0) {
pthread_join(_adjustTid, NULL);
}
for (int i = 0; i < _liveThreadNum; ++i) {
//通知所有空闲线程
pthread_cond_broadcast(&_queueNotEmpty);
}
for (int i = 0; i < _liveThreadNum; i++) {
printf("thread %d is waiting\n", _threads[i].x);
pthread_cancel(_threads[i]); //通知子线程结束
pthread_join(_threads[i], NULL);
}
threadPoolFree();
return true;
}
ThreadPool::~ThreadPool()
{
threadPoolDestroy();
}
void ThreadPool::addTask(ThreadTask *task)
{
pthread_mutex_lock(&(_lock));
/* ==为真,队列已经满, 调wait阻塞 */
while ((_queueSize == _queueMaxSize) && (!_shutdown)) {
pthread_cond_wait(&(_queueNotFull), &(_lock));//阻塞
}
if (_shutdown) {
pthread_mutex_unlock(&(_lock));
}
if ((*_taskQueue)[_queueRear] != nullptr) {
//队列已经满了
}
(*_taskQueue)[_queueRear] = task;
_queueRear = (_queueRear + 1) % _queueMaxSize; /* 队尾指针移动, 模拟环形 */
_queueSize++;
/*添加完任务后,队列不为空,唤醒线程池中 等待处理任务的线程*/
pthread_cond_signal(&(_queueNotEmpty));
pthread_mutex_unlock(&(_lock));
}
void * ThreadPool::threadpoolTask(void * threadpool)
{
ThreadPool* pool = (ThreadPool*)threadpool;
ThreadTask* task;
while (true)
{
pthread_mutex_lock(&(pool->_lock));
/*_queueSize == 0 说明没有任务,调 wait 阻塞在条件变量上, 若有任务,跳过该while*/
while (pool->_queueSize == 0 && (!pool->_shutdown))
{
printf("thread %d is waiting\n", pthread_self());
pthread_cond_wait(&(pool->_queueNotEmpty), &(pool->_lock));
/*清除指定数目的空闲线程,如果要结束的线程个数大于0,结束线程*/
if (pool->_waitExitThreadNum > 0) {
pool->_waitExitThreadNum--;
/*如果线程池里线程个数大于最小值时可以结束当前线程*/
if (pool->_liveThreadNum > pool->_minimumPoolSize) {
printf("thread %d is exiting\n", pthread_self());
pool->_liveThreadNum--;
pthread_mutex_unlock(&(pool->_lock));
pthread_exit(NULL);
}
}
}
/*如果指定了true,要关闭线程池里的每个线程,自行退出处理*/
if (pool->_shutdown) {
pthread_mutex_unlock(&(pool->_lock));
printf("thread %d is exiting\n", pthread_self());
pthread_exit(NULL); /* 线程自行结束 */
}
task = (*pool->_taskQueue)[pool->_queueFront];
(*pool->_taskQueue)[pool->_queueFront] = nullptr;
pool->_queueFront = (pool->_queueFront + 1) % pool->_queueMaxSize;
pool->_queueSize--;
/*通知可以有新的任务添加进来*/
pthread_cond_broadcast(&(pool->_queueNotFull));
/*任务取出后,立即将 线程池琐 释放*/
pthread_mutex_unlock(&(pool->_lock));
/*执行任务*/
printf("thread %d start working\n", pthread_self());
pthread_mutex_lock(&(pool->_threadCounter)); /*忙状态线程数变量琐*/
pool->_busyThreadNum++; /*忙状态线程数+1*/
pthread_mutex_unlock(&(pool->_threadCounter));
task->threadTask();
/*任务结束处理*/
printf("thread %d end working\n", pthread_self());
pthread_mutex_lock(&(pool->_threadCounter));
pool->_busyThreadNum--; /*处理掉一个任务,忙状态数线程数-1*/
pthread_mutex_unlock(&(pool->_threadCounter));
}
pthread_exit(NULL);
return nullptr;
}
void * ThreadPool::adjustThread(void * threadpool)
{
ThreadPool* pool = (ThreadPool*)threadpool;
while (!pool->_shutdown)
{
Sleep(CIRCLETIME); //每隔5s扫描一次
pthread_mutex_lock(&(pool->_lock));
int queue_size = pool->_queueSize; /* 关注 任务数 */
int live_thr_num = pool->_liveThreadNum; /* 存活 线程数 */
pthread_mutex_unlock(&(pool->_lock));
pthread_mutex_lock(&(pool->_threadCounter));
int busy_thr_num = pool->_busyThreadNum; /* 忙着的线程数 */
pthread_mutex_unlock(&(pool->_threadCounter));
if (busy_thr_num / (double)live_thr_num > pool->_maxThreshold) {
pthread_mutex_lock(&(pool->_lock));
int addNum = pool->_weight*busy_thr_num;
for (int i = 0; i < pool->_maximumPoolSize && addNum&&pool->_liveThreadNum<pool->_maximumPoolSize; ++i) {
//增加线程
//pool->_threads[i].p == 0 ||
if (pool->_threads[i].x != 0|| !pool->isAlive(pool->_threads[i])) {
pthread_create(&(pool->_threads[i]), NULL, pool->threadpoolTask, (void *)pool);
addNum--;
pool->_liveThreadNum++;
}
}
pthread_mutex_unlock(&(pool->_lock));
}
if (busy_thr_num / (double)live_thr_num < pool->_minThreshold) {
//减少存在的线程
pthread_mutex_lock(&(pool->_lock));
int deleteNum = pool->_weight*live_thr_num;
pool->_waitExitThreadNum = deleteNum; /* 要销毁的线程数 设置为10 */
pthread_mutex_unlock(&(pool->_lock));
for (int i = 0; i < deleteNum; i++) {
/* 通知处在空闲状态的线程, 他们会自行终止*/
pthread_cond_signal(&(pool->_queueNotEmpty));
}
}
}
return nullptr;
}
bool ThreadPool::isAlive(pthread_t tid)
{
int kill_rc = pthread_kill(tid, 0); //发0号信号,测试线程是否存活
if (kill_rc == ESRCH) {
return false;
}
return true;
}
ThreadTask声明的头文件
#pragma once
#define HAVE_STRUCT_TIMESPEC
#include <stdio.h>
#include <pthread.h>
#include <assert.h>
#include <vector>
#pragma comment(lib, "pthreadVC2.lib")
class ThreadTask
{
public:
ThreadTask();
//加入这个函数的目的是,取消线程的时候,线程池会发出一个CANCEL的信号
//我们需要有接受这个信号的方法,从而结束线程,以防止子线程陷入死循环
void threadTask();
virtual void doTask() = 0;
virtual ~ThreadTask();
};
ThreadTask定义的Cpp
#include "ThreadTask.h"
ThreadTask::ThreadTask()
{
}
void ThreadTask::threadTask()
{
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
this->doTask();
}
ThreadTask::~ThreadTask()
{
}
测试的主函数
//main.cpp
#define HAVE_STRUCT_TIMESPEC
#include <stdio.h>
#include <pthread.h>
#include <assert.h>
#include<Windows.h>
#include<iostream>
#include"ThreadPool.h"
#include"ThreadTask.h"
#pragma comment(lib, "pthreadVC2.lib")
class TaskA :public ThreadTask{
public:
TaskA(int num=0):num(num){}
virtual void doTask() {
std::cout << "我是线程A num=:" << num << std::endl;
return;
}
private:
int num;
};
class TaskB :public ThreadTask {
public:
TaskB(int num = 0) :num(num) {}
virtual void doTask() {
std::cout << "我是线程B num=:" << num << std::endl;
while (true)
{
}
return;
}
private:
int num;
};
int main()
{
ThreadPool threadPool(5,40);//不建议太大
std::vector<ThreadTask *>temp; //防止内存泄漏,这里没起作用,以防万一,因为下面的a被使用后就没了。
threadPool.addAdjustThread();
for (int i = 0; i < 110; ++i) {
ThreadTask *a = new TaskB(i);
//ThreadTask *a = new TaskA(i);
threadPool.addTask(a);
temp.push_back(a);
}
std::cout << threadPool.getLiveNum() << std::endl;
std::cout << threadPool.getBusyNum() << std::endl;
Sleep(1000);
std::cout << threadPool.getLiveNum() << std::endl;
std::cout << threadPool.getBusyNum() << std::endl;
Sleep(1000);
std::cout << threadPool.getLiveNum() << std::endl;
std::cout << threadPool.getBusyNum() << std::endl;
system("pause");
return 0;
}
小结
本文主要通过使用pthread库实现了线程池,当然C++11支持thread创建多线程的方式。因为是学习阶段,这只是初步的尝试,后期如果有什么建议大家可以指出,如果建议较多可以慢慢完善。如果有需求,下一步计划完全使用C++11及以上的版本实现线程池,然后不断的完善。如果能做成一个好用的模板当然更好,有什么意见大家可以多多提。特别是bug。我自己在测试的时候发现线程到50-100的时候CPU的使用率直线上升。可能有bug希望大佬能提出来。