c语言-自定义线程池
文章目录
为什么使用线程池
多线程编程时,需要通过多个线程来处理多个任务。如果不经过优化,那么最简单的想法应该是每到来一个任务,就创建一个线程用来处理这个任务,当这个任务处理完毕,就销毁这个线程。但是这个简单的想法会带来大量的时空开销。设想,假如有成百上千个任务,为了处理它们,岂不是要创建和销毁线程成百上千次,这显然是不划算的。为了减小创建和销毁线程的开销,很容易就可以想到利用"池“结构来优化。所谓的“池”结构,就是我们预先创建好多个线程。当有任务到来时,把这些任务存储起来,与此同时,让空闲的线程去选择任务执行它。这就是线程池的大致思想。创建的线程自线程池创建以后就一直存在,直到线程池被销毁为止,这样就避免了重复创建销毁的开销。
线程的执行方式是需要特别注意:
- 线程的执行: 一些人对于线程池的使用可能有一些误解,它们认为线程池是通过从线程池中选择线程来执行手上的任务。其实更加形象的说法应该是我们将任务放入线程池的任务队列,然后线程池中的线程就会自动竞争执行任务。为了达到这一点,我们可以把任务队列视为临界资源,利用生产者和消费者模型来解决。添加任务的程序是生产者,工作线程是消费者。所以工作线程一共有两种状态
- 运行态:取到任务以后开始执行。
- 阻塞态:没取到任务,可能是因为没有获得互斥锁,也可能是因为当前任务队列中没有任务可以执行。
头文件
#ifndef STUDY_THREAD_POOL_H
#define STUDY_THREAD_POOL_H
#include "../structure/task_queue.h"
#include <pthread.h>
// 线程池结构体
typedef struct threadPool
{
// 任务队列
Queue* taskQ;
int queueCapacity; // 容量
pthread_t managerID; // 管理者线程ID
pthread_t* threadIDs; // 工作的线程IDs
int minNum; // 最小线程数量
int maxNum; // 最大线程数量
int busyNum; // 忙的线程的个数
int liveNum; // 存活的线程的个数
int exitNum; // 要销毁的线程个数
pthread_mutex_t mutexPool; // 锁整个的线程池
pthread_mutex_t mutexBusy; // 锁busyNum变量
pthread_cond_t notEmpty; // 任务队列是不是空了
pthread_cond_t notFull; // 任务队列是不是满了
int shutdown; // 是不是要销毁线程池, 销毁为1, 不销毁为0
} ThreadPool ;
// 创建线程池并初始化
ThreadPool* threadPoolCreate(int min, int max, int queueSize);
// 销毁线程池
int threadPoolDestroy(ThreadPool* pool);
// 给线程池添加任务
int threadPoolAdd(ThreadPool* pool, void(*func)(void*), void* arg);
// 获取线程池中工作的线程的个数
int threadPoolBusyNum(ThreadPool* pool);
// 获取线程池中活着的线程的个数
int threadPoolAliveNum(ThreadPool* pool);
void threadPoolWait(ThreadPool *pool);
#endif //STUDY_THREAD_POOL_H
内容实现
#include <malloc.h>
#include "thread_pool.h"
#include <stdio.h>
#include <unistd.h>
static void threadExit(ThreadPool *pool) {
pthread_t tid = pthread_self();
for (int i = 0; i < pool->maxNum; ++i) {
if (pool->threadIDs[i] == tid) {
pool->threadIDs[i] = 0;
printf("threadExit() called, %llu exiting...\n", tid);
break;
}
}
pthread_exit(NULL);
}
static void *threadPoolFunction(void *arg) {
ThreadPool *pool = (ThreadPool *) arg;
while (1) {
pthread_mutex_lock(&pool->mutexPool);//加锁整个线程池
//如果线程池中没有任务,且线程池没有销毁,那么就等待
while (pool->taskQ->size == 0 && !pool->shutdown) {
// 阻塞工作线程
pthread_cond_wait(&pool->notEmpty, &pool->mutexPool);
// 如果线程池要销毁,那么就退出
if (pool->exitNum > 0) {
pool->exitNum--;
if (pool->liveNum > pool->minNum) {
pool->liveNum--;
pthread_mutex_unlock(&pool->mutexPool);
threadExit(pool);
}
}
}
// 判断线程池是否被关闭了
if (pool->shutdown) {
pthread_mutex_unlock(&pool->mutexPool);
threadExit(pool);
}
// 从任务队列中取出一个任务
Task *task = dequeue(pool->taskQ);
// 解锁
pthread_cond_signal(&pool->notFull);
pthread_mutex_unlock(&pool->mutexPool);
// printf("thread %llu start working...\n", pthread_self());
pthread_mutex_lock(&pool->mutexBusy);
pool->busyNum++;
pthread_mutex_unlock(&pool->mutexBusy);
task->function(task->arg);
free(task->arg);
task->arg = NULL;
// printf("thread %llu end working...\n", pthread_self());
pthread_mutex_lock(&pool->mutexBusy);
pool->busyNum--;
pthread_mutex_unlock(&pool->mutexBusy);
}
return NULL;
}
static void *manager(void *arg) {
ThreadPool *pool = (ThreadPool *) arg;
while (!pool->shutdown) {
// 每隔3s检测一次
sleep(3);
// 取出线程池中任务的数量和当前线程的数量
pthread_mutex_lock(&pool->mutexPool);
int queueSize = pool->taskQ->size;
int liveNum = pool->liveNum;//当前存活的线程数
pthread_mutex_unlock(&pool->mutexPool);
// 取出忙的线程的数量
pthread_mutex_lock(&pool->mutexBusy);
int busyNum = pool->busyNum;
pthread_mutex_unlock(&pool->mutexBusy);
// 如果任务的数量大于0,并且存活的线程数小于最大线程数,那么就创建新的线程
if (queueSize > 0 && liveNum < pool->maxNum) {
pthread_mutex_lock(&pool->mutexPool);
for (int i = 0; i < pool->maxNum && pool->liveNum < pool->maxNum; ++i) {
if (pool->threadIDs[i] == 0) {
//找到一个空闲的线程id,创建线程
pthread_create(&pool->threadIDs[i], NULL, threadPoolFunction, pool);
pool->liveNum++;
}
}
pthread_mutex_unlock(&pool->mutexPool);
}
// 销毁线程 忙的线程*2 < 存活的线程数 && 存活的线程>最小线程数
if (busyNum * 2 < liveNum && liveNum > pool->minNum) {
pthread_mutex_lock(&pool->mutexPool);
pool->exitNum = busyNum; // 要销毁的线程数,空闲线程内部会自动判断是否销毁
pthread_mutex_unlock(&pool->mutexPool);
// 唤醒所有空闲线程,让线程自动销毁,一定的数量
pthread_cond_broadcast(&pool->notEmpty);
}
}
return NULL;
}
ThreadPool *threadPoolCreate(int min, int max, int queueSize) {
ThreadPool *pool = (ThreadPool *) malloc(sizeof(ThreadPool));
pool->minNum = min;
pool->maxNum = max;
pool->liveNum = min;
pool->queueCapacity = queueSize;
pool->shutdown = 0;
pool->taskQ = createQueue();
pool->threadIDs = (pthread_t *) malloc(sizeof(pthread_t) * max);
pthread_mutex_init(&(pool->mutexPool), NULL);
pthread_cond_init(&(pool->notFull), NULL);
if (pthread_mutex_init(&pool->mutexPool, NULL) != 0 ||
pthread_mutex_init(&pool->mutexBusy, NULL) != 0 ||
pthread_cond_init(&pool->notEmpty, NULL) != 0 ||
pthread_cond_init(&pool->notFull, NULL) != 0 )
{
printf("mutex or condition init fail...\n");
return NULL;
}
int i;
for (i = 0; i < min; i++) {
pthread_create(&(pool->threadIDs[i]), NULL, threadPoolFunction, (void *) pool);
}
pthread_create(&(pool->managerID), NULL, manager, (void *) pool);
return pool;
}
// 存活的线程的个数
int threadPoolAliveNum(ThreadPool *pool) {
pthread_mutex_lock(&pool->mutexPool);
int aliveNum = pool->liveNum;
pthread_mutex_unlock(&pool->mutexPool);
return aliveNum;
}
// 忙的线程的个数
int threadPoolBusyNum(ThreadPool *pool) {
pthread_mutex_lock(&pool->mutexBusy);
int busyNum = pool->busyNum;
pthread_mutex_unlock(&pool->mutexBusy);
return busyNum;
}
//添加任务,成功返回1,失败返回-1
int threadPoolAdd(ThreadPool *pool, void(*func)(void *), void *arg) {
pthread_mutex_lock(&pool->mutexPool);//加锁
// 如果任务队列满了,就等待
while (pool->taskQ->size == pool->queueCapacity && !pool->shutdown) {
// 阻塞生产者线程
pthread_cond_wait(&pool->notFull, &pool->mutexPool);
}
// 判断线程池是否关闭
if (pool->shutdown) {
pthread_mutex_unlock(&pool->mutexPool);
return -1;
}
// 添加任务
enqueue(pool->taskQ, createTask(func, arg));
pthread_cond_signal(&pool->notEmpty); // 唤醒一个线程执行任务
pthread_mutex_unlock(&pool->mutexPool);//解锁
return 1;
}
// 销毁线程池 成功返回1,失败返回-1
int threadPoolDestroy(ThreadPool *pool) {
if (pool == NULL) {
return -1;
}
// 关闭线程池
pool->shutdown = 1;
// 阻塞等待回收管理者线程执行完毕
pthread_join(pool->managerID, NULL);
// 唤醒阻塞的所有线程
pthread_cond_broadcast(&pool->notEmpty);
// 释放堆内存
if (pool->taskQ) {
destroyQueue(pool->taskQ);
}
if (pool->threadIDs) {
free(pool->threadIDs);
}
pthread_mutex_destroy(&pool->mutexPool);
pthread_mutex_destroy(&pool->mutexBusy);
pthread_cond_destroy(&pool->notEmpty);
pthread_cond_destroy(&pool->notFull);
free(pool);
pool = NULL;
return 1;
}
//等待所有任务完成
void threadPoolWait(ThreadPool *pool) {
if (pool == NULL) {
return;
}
while (pool->taskQ->size > 0||pool->busyNum>0) {
sleep(1);
}
}