之前在20 Linux线程池 一文中曾经实现过基于环形队列的线程池。
但是并不是很完美,因为并没有实现子线程退出的功能,子线程只能随着进程一块退出,并且扩容和增容也没有实现。
本文中的基于链表的线程池对线程池进行了进一步封装,解决了这些问题。
总体结构
该线程池一共有三个类型:
- 任务类,也就是要执行的回调函数、以及执行回调函数需要的数据。
//任务节点
struct nJob
{
//回调函数,用来处理不同的任务
void (*job_function)(nJob *job);
void *user_data;
//前后指针,使得节点形成链表
nJob *prev;
nJob *next;
};
- 工作类,该类型中有一个线程池的指针,能通过线程池找到任务类,从而获取任务然后执行。
class nThreadPool;
//执行队列节点
struct nWorker
{
//线程的id
pthread_t thread = 0;
//判断是否终止退出
bool isexit = false;
//指向线程池
nThreadPool *workqueue = nullptr;
nWorker *prev = nullptr;
nWorker *next = nullptr;
};
- 线程池类,该类型主要负责管理和控制任务类和工作类,并且通过互斥锁和信号量,保证工作类的调度运行。
class nThreadPool
{
private:
//链表头插
template <class T>
static void addListNode(T *&item, T *&list);
//从链表中删除某个节点
template <class T>
static void eraseListNode(T *&item, T *&list);
//任务队列和执行队列
nWorker *workers;
nJob *waiting_jobs;
pthread_mutex_t jobs_mtx;
pthread_cond_t jobs_cond;
//当前任务队列中任务的数量
int jobs_num = 0;
//当前执行队列中线程的数量
atomic<int> worker_num;
//最大线程的数量
#define max_worker_num 20
//销毁线程池若干个节点
void deleteThreadPoolShutdown();
void eraseTthrad(int del_num);
void increaseThread(int numWorkers);
public:
//是否需要增加或者减少线程数量
void isChange();
~nThreadPool()
{
deleteThreadPoolShutdown();
}
//回调函数
static void *callbackWorkerThread(void *ptr);
//创建线程池
int initThreadPoolCreate(int numWorkers);
//加入任务
void addThreadPoolQueue(nJob *job);
bool isEmpty();
int getJobNums();
atomic<int> &getWorkerNums();
};
void func(nJob *job)
{
int index = *(int *)job->user_data;
std::cout << "thread is[" << pthread_self() << "]," << index << " pow is:" << pow(index, 2) << std::endl;
free(job->user_data);
free(job);
sleep(1);
}
int main(int argc, char *argv[])
{
clock_t begin = clock();
nThreadPool *pool = new nThreadPool;
pool->initThreadPoolCreate(5);
int i = 0;
for (i = 0; i < 1000; i++)
{
nJob *job = (nJob *)malloc(sizeof(nJob));
if (job == NULL)
{
perror("malloc");
exit(1);
}
job->job_function = func;
job->user_data = malloc(sizeof(int));
*(int *)job->user_data = i;
pool->addThreadPoolQueue(job);
}
while (!pool->isEmpty())
{
// cout << "当前线程中任务节点数量:" << pool->getJobNums() << endl;
pool->isChange();
sleep(1);
}
delete pool;
sleep(3);
clock_t end = clock();
cout << "总用时:" << ((double)end - (double)begin) / CLOCKS_PER_SEC * 1000 << "ms" << endl;
}
这里采用了伪删除的思路,每个工作节点有一个bool变量,当该变量为true时,则在下次获取任务之前就退出该线程。
有了这样的删除思路,剩下的增容和减容,也就好实现了。这里以增加一倍和减少一倍为例:
代码实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <iostream>
#include <unistd.h>
#include <time.h>
#include <atomic>
#include <cmath>
using namespace std;
class nThreadPool;
//执行队列节点
struct nWorker
{
//线程的id
pthread_t thread = 0;
//判断是否终止退出
bool isexit = false;
//指向线程池
nThreadPool *workqueue = nullptr;
nWorker *prev = nullptr;
nWorker *next = nullptr;
};
//任务节点
struct nJob
{
//回调函数,用来处理不同的任务
void (*job_function)(nJob *job);
void *user_data;
//前后指针,使得节点形成队列
nJob *prev = nullptr;
nJob *next = nullptr;
};
class nThreadPool
{
private:
//链表头插
template <class T>
static void addListNode(T *&item, T *&list)
{
item->prev = nullptr;
item->next = list;
if (list != nullptr)
{
list->prev = item;
}
list = item;
}
//从链表中删除某个节点
template <class T>
static void eraseListNode(T *&item, T *&list)
{
if (item->prev != nullptr)
item->prev->next = item->next;
if (item->next != nullptr)
item->next->prev = item->prev;
if (list == item)
{
list = item->next;
}
item->prev = item->next = nullptr;
}
//任务队列和执行队列
nWorker *workers;
nJob *waiting_jobs;
pthread_mutex_t jobs_mtx;
pthread_cond_t jobs_cond;
//当前任务队列中任务的数量
int jobs_num = 0;
//当前执行队列中线程的数量
atomic<int> worker_num;
//最大线程的数量
#define max_worker_num 20
//销毁线程池若干个节点
void deleteThreadPoolShutdown()
{
cout << "开始销毁线程池-------------------------" << endl;
nWorker *worker = NULL;
for (worker = this->workers; worker != NULL; worker = worker->next)
{
worker->isexit = true;
}
// pthread_mutex_lock(&this->jobs_mtx);
this->workers = worker;
pthread_cond_broadcast(&this->jobs_cond);
// pthread_mutex_unlock(&this->jobs_mtx);
}
void eraseTthrad(int del_num)
{
nWorker *worker = NULL;
for (worker = this->workers; worker != NULL && del_num != 0; worker = worker->next, del_num--)
{
worker->isexit = true;
}
this->workers = worker;
}
void increaseThread(int numWorkers)
{
//接下来开始创建执行队列(线程)
int i = 0;
for (i = this->worker_num; i < numWorkers; i++)
{
nWorker *worker = (nWorker *)malloc(sizeof(nWorker));
if (worker == NULL)
{
perror("malloc");
return;
}
memset(worker, 0, sizeof(nWorker));
worker->workqueue = this;
//创建线程,设置回调函数
int ret = pthread_create(&worker->thread, NULL, callbackWorkerThread, (void *)worker);
if (ret)
{
perror("pthread_create");
free(worker);
return;
}
//将执行的节点,放到执行队列中
addListNode(worker, worker->workqueue->workers);
}
worker_num = numWorkers;
}
public:
//是否需要增加或者减少线程数量
void isChange()
{
//如果任务的数量是线程数量的三倍,则增加一倍的线程
if (this->jobs_num > this->worker_num * 3)
{
// cout << "开始扩容,work数量:" << this->worker_num << "任务数量:" << this->jobs_num << endl;
increaseThread(this->worker_num * 2);
// cout << "扩容成功,当前work数量:" << this->worker_num << endl;
}
//如果线程比最大线程数量要大,则减少一半的线程数量
else if ((this->worker_num > max_worker_num))
{
// cout << "开始减容,work数量:" << this->worker_num << "最大数量:" << max_worker_num << endl;
//减容
eraseTthrad(this->worker_num / 2);
this->worker_num = max_worker_num;
}
}
~nThreadPool()
{
deleteThreadPoolShutdown();
}
//回调函数
static void *callbackWorkerThread(void *ptr)
{
nWorker *worker = (nWorker *)ptr;
//不断去任务队列中取任务
while (1)
{
//需要加锁,因为任务队列是临界资源
pthread_mutex_lock(&worker->workqueue->jobs_mtx);
//任务队列中没任务,等待
while (worker->workqueue->waiting_jobs == NULL)
{
//当前执行队列的节点需要被释放了
if (worker->isexit)
{
break;
}
pthread_cond_wait(&worker->workqueue->jobs_cond, &worker->workqueue->jobs_mtx);
}
//当前执行队列的节点需要被释放了
if (worker->isexit)
{
pthread_mutex_unlock(&worker->workqueue->jobs_mtx);
break;
}
//取出要执行的节点,执行回调函数
nJob *job = worker->workqueue->waiting_jobs;
if (job != NULL)
{
eraseListNode(job, worker->workqueue->waiting_jobs);
worker->workqueue->jobs_num--;
pthread_mutex_unlock(&worker->workqueue->jobs_mtx);
}
else
{
pthread_mutex_unlock(&worker->workqueue->jobs_mtx);
continue;
}
job->job_function(job);
}
//释放对应的执行节点
eraseListNode(worker, worker->workqueue->workers);
free(worker);
// cout << "释放了一个线程" << endl;
pthread_exit(NULL);
}
//创建线程池
int initThreadPoolCreate(int numWorkers)
{
if (numWorkers < 1)
numWorkers = 1;
memset(this, 0, sizeof(nThreadPool));
//初始化条件变量和锁
pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER;
memcpy(&this->jobs_cond, &blank_cond, sizeof(this->jobs_cond));
pthread_mutex_t blank_mutex = PTHREAD_MUTEX_INITIALIZER;
memcpy(&this->jobs_mtx, &blank_mutex, sizeof(this->jobs_mtx));
//接下来开始创建执行队列(线程)
int i = 0;
for (i = 0; i < numWorkers; i++)
{
nWorker *worker = (nWorker *)malloc(sizeof(nWorker));
if (worker == NULL)
{
perror("malloc");
return 1;
}
memset(worker, 0, sizeof(nWorker));
worker->workqueue = this;
//创建线程,设置回调函数
int ret = pthread_create(&worker->thread, NULL, callbackWorkerThread, (void *)worker);
if (ret)
{
perror("pthread_create");
free(worker);
return 1;
}
//将执行的节点,放到执行队列中
addListNode(worker, worker->workqueue->workers);
}
worker_num = numWorkers;
return 0;
}
//加入任务
void addThreadPoolQueue(nJob *job)
{
pthread_mutex_lock(&this->jobs_mtx);
addListNode(job, this->waiting_jobs);
this->jobs_num++;
pthread_cond_signal(&this->jobs_cond);
pthread_mutex_unlock(&this->jobs_mtx);
}
bool isEmpty()
{
return this->jobs_num == 0;
}
int getJobNums()
{
return this->jobs_num;
}
atomic<int> &getWorkerNums()
{
return this->worker_num;
}
};
void func(nJob *job)
{
int index = *(int *)job->user_data;
std::cout << "thread is[" << pthread_self() << "]," << index << " pow is:" << pow(index, 2) << std::endl;
free(job->user_data);
free(job);
sleep(1);
}
int main(int argc, char *argv[])
{
clock_t begin = clock();
nThreadPool *pool = new nThreadPool;
pool->initThreadPoolCreate(5);
int i = 0;
for (i = 0; i < 1000; i++)
{
nJob *job = (nJob *)malloc(sizeof(nJob));
if (job == NULL)
{
perror("malloc");
exit(1);
}
job->job_function = func;
job->user_data = malloc(sizeof(int));
*(int *)job->user_data = i;
pool->addThreadPoolQueue(job);
}
while (!pool->isEmpty())
{
// cout << "当前线程中任务节点数量:" << pool->getJobNums() << endl;
pool->isChange();
sleep(1);
}
delete pool;
sleep(3);
clock_t end = clock();
cout << "总用时:" << ((double)end - (double)begin) / CLOCKS_PER_SEC * 1000 << "ms" << endl;
}