提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
一、线程池是什么?
线程池是一种管理和复用线程的机制,它以一组预先创建的线程来执行任务,而不是为每个任务都创建一个新线程。线程池的基本原理如下:
初始化线程池:在线程池被创建时,会初始化一定数量的线程,并将它们置于就绪状态,等待任务的到来。
接收任务:当有任务需要执行时,任务会被提交到线程池中。线程池管理器会从任务队列中获取任务。
线程分配任务:线程池管理器从可用的线程池中选择一个空闲的线程,将任务分配给该线程进行执行。
任务执行:被选中的线程会执行被分配的任务。执行完任务后,线程并不会被销毁,而是继续保持活动状态,等待下一个任务的到来。
任务完成和回收:当任务执行完毕后,线程池管理器会将线程标记为空闲状态,并将线程返回到线程池中,以供下一个任务使用。
通过使用线程池,可以获得以下几个优点:
减少线程创建和销毁的开销:线程的创建和销毁是有成本的,通过线程池可以减少这些开销。预先创建一组线程,并重用它们,避免频繁地创建和销毁线程。
控制并发线程数量:线程池可以限制并发执行的线程数量,避免因为线程过多而导致系统资源耗尽。
提高任务调度效率:通过任务队列和线程池管理器,可以更好地控制任务执行的顺序和优先级,提高任务调度和执行的效率。
提供线程管理和监控功能:线程池还可以提供监控线程状态、统计任务执行情况、灵活调整线程池参数等管理功能。
需要注意的是,在使用线程池时,应根据实际需求合理配置线程池的大小和相关参数,以避免线程饥饿、线程过多或任务阻塞等问题。同时,在设计任务时,要考虑线程安全性和任务的可分解性,以便更好地发挥线程池的性能优势。
二、使用步骤
1.创建任务和线程池结构体
代码如下(示例):
//任务结构体
struct task_s
{
/* data */
void (*function)(void *arg); // 传入函数
void *arg; // 函数参数
};
// 线程池结构体
struct ThreadPool_s
{
Task* taskQ; // 任务队列
int queueCapacity; // 队列容量
int queueSize; // 当前任务数量
int queueFront; // 队头
int queueRear; // 队尾
pthread_t managerID; // 管理者线程ID
pthread_t* threadIDs; // 工作者线程ID
int minNum; // 最小线程数量
int maxNum; // 最大线程数量
int busyNum; // 忙的线程的个数
int liveNum; // 存活的线程个数
int exitNum; // 要结束的线程个数
pthread_mutex_t mutexPool; // 锁整个的线程池
pthread_mutex_t mutexBusy; // 锁busyNum变量
pthread_cond_t notFull; // 任务队列是不是满了
pthread_cond_t notEmpty; // 任务队列是不是空了
int shutdown; //是否销毁线程池,销毁为1,不销毁为0
};
typedef struct ThreadPool_s ThreadPool; // 结构体类型起别名
2.线程池初始化
代码如下(示例):
/*
参数一:线程池最小线程数量
参数二:线程池最大线程数量
参数三:一个线程能够执行任务数
初始化成功返回线程池指针对象,失败返回NULL
*/
ThreadPool *threadPoolCreate(const int min, const int max, const int queueSize)
{
ThreadPool* pool = (ThreadPool *)calloc(sizeof(ThreadPool), 1); // 创建线程池指针对象并开辟空间
do
{
if (pool == NULL) // 如果分配空间失败,跳出do...while(0)
{
perror("fail to create ThreadPool");
break;
}
if((pool->threadIDs = (pthread_t *)calloc(sizeof(pthread_t), max)) == NULL)
{
perror("fail to create threadIDs");
break;
}
pool->minNum = min;
pool->maxNum = max;
pool->busyNum = 0;
pool->liveNum = min;
pool->exitNum = 0;
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)
{
perror("fail to mutex or cond");
break;
}
pool->taskQ = (Task *)calloc(sizeof(Task), queueSize);
pool->queueCapacity = queueSize;
pool->queueSize = 0;
pool->queueFront = 0;
pool->queueRear = 0;
pool->shutdown = 0;
pthread_create(&pool->managerID, NULL, manager, pool); // manager为管理者函数,只有一个
for (int i=0; i<min; i++)
{
pthread_create(&pool->threadIDs[i], NULL, worker, pool); // worker为工作者线程,具有多个
}
return pool;
}while(0);
if (pool && pool->threadIDs) free(pool->threadIDs);
if (pool && pool->taskQ) free(pool->taskQ);
if (pool) free(pool);
return NULL;
}
3. 工作者和管理者
代码如下(示例):
// 一次减少或者增加的线程数为2
#define NUMBER 2
static void *worker(void *arg)
{
ThreadPool *pool = (ThreadPool *)arg;
while(1)
{
pthread_mutex_lock(&pool->mutexPool);
// 当前任务队列是否为空
while (pool->queueSize == 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;
task.function = pool->taskQ[pool->queueFront].function;
task.arg = pool->taskQ[pool->queueFront].arg;
pool->queueFront = (pool->queueFront + 1) % pool->queueCapacity;
pool->queueSize--;
pthread_cond_signal(&pool->notFull); // 唤醒管理者
pthread_mutex_unlock(&pool->mutexPool);
printf("thread %ld start working\r\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;
//(*task.function)(task.arg); // 解引用
printf("thread %ld end working\r\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)
{
sleep(3);
pthread_mutex_lock(&pool->mutexPool);
int queueSize = pool->queueSize;
int liveNum = pool->liveNum;
pthread_mutex_unlock(&pool->mutexPool);
pthread_mutex_lock(&pool->mutexBusy);
int busyNum = pool->busyNum;
pthread_mutex_unlock(&pool->mutexBusy);
// 线程少了
if (queueSize > liveNum && liveNum < pool->maxNum)
{
int counter = 0;
pthread_mutex_lock(&pool->mutexPool);
for (int i=0; i<pool->maxNum && counter<NUMBER && pool->liveNum<pool->maxNum; ++i)
{
if (pool->threadIDs[i] == 0)
{
pthread_create(&pool->threadIDs[i], NULL, worker, pool); // 唤醒工作者,诱导其自动关闭
counter++;
pool->liveNum++;
}
}
pthread_mutex_unlock(&pool->mutexPool);
}
// 线程多了
if (busyNum*2 < liveNum && liveNum > pool->minNum)
{
pthread_mutex_lock(&pool->mutexPool);
pool->exitNum = NUMBER;
pthread_mutex_unlock(&pool->mutexPool);
for (int i=0; i < NUMBER; ++i)
{
pthread_cond_signal(&pool->notEmpty); // 唤醒管理者
}
}
}
return NULL;
}
4.线程退出函数
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, %ld exiting...\n", tid);
break;
}
}
pthread_exit(NULL);
}
5.其余操作函数
// 获取线程池中工作的线程的个数
int threadPoolBushNum(ThreadPool *pool)
{
int busyNum = 0;
pthread_mutex_lock(&pool->mutexPool);
busyNum = pool->busyNum;
pthread_mutex_unlock(&pool->mutexPool);
return busyNum;
}
// 获取线程池中活着的线程的个数
int threadPoolAliveNum(ThreadPool *pool)
{
int liveNum = 0;
pthread_mutex_lock(&pool->mutexPool);
liveNum = pool->liveNum;
pthread_mutex_unlock(&pool->mutexPool);
return liveNum;
}
5. 主函数测试
#include "./main.h"
#include "./threadpool.h"
void taskFunc(void *arg);
int main(int argc, char *argv[])
{
ThreadPool *Pool = threadPoolCreate(3, 10, 100);;
for (int i = 0; i < 100; i++)
{
int *num = (int *)calloc(sizeof(int), 1);
*num = i + 100;
threadPoolAdd(Pool, taskFunc, num);
}
sleep(30); // 让主线程一直执行
threadPoolDestory(Pool);
return 0;
}
void taskFunc(void *arg)
{
int num = *(int *)arg;
printf("thread %ld is working, tid = %d\n", pthread_self(), num);
sleep(1);
}
总结
经过测试,线程池可以使用。