线程池
概念
线程池就是首先创建一些线程,它们的集合称为线程池。使用线程池可以很好的提高性能,线程池在系统启动时创建大量空闲的线程,当任务池有任务时,线程池中的线程排队去领取任务池的任务,如果当前没有任务就阻塞等待。
工作机制
- 在有线程池的情况下,任务是交给线程池而不是直接交给某个线程,在线程池拿到任务后,寻找是否有空闲的线程,如果有就交付给该线程执行。
- 一个线程,只能同时执行一个任务,执行完毕后,线程重回空闲状态,等待下一个任务。
使用线程池的原因
多线程运行时间,系统不断启动和关闭线程,成本很高,会过度消耗线程资源,以及过度切换线程的危险,从而导致系统资源的崩溃。
图解线程池
代码
线程池结构
typedef struct NMANAGER
{
struct NWORKER *workers; // 管理的线程链表头节点
struct NJOBS *jobs; //任务池头节点
int totWorkers; // 当前一共拥有多少线程
int idleWorkers; // 当前空闲线程数量
pthread_mutex_t pool_mtx; // 线程池互斥锁
pthread_cond_t pool_cond; // 线程池条件锁,所有线程等待这个条件锁
} nManager;
线程结构
typedef struct NWORKER
{
pthread_t pthreadid; //该线程的线程id
struct NMANAGER *pool; // 处于哪一个线程池
int terminate; // 是否被终止,1为销毁该线程,0则相反
struct NWORKER *next; //链表中的下一个线程
struct NWORKER *prev;//链表中的上一个线程
} nWorker;
任务池
typedef struct NJOBS
{
void (*func)(void *arg); //任务回调,线程执行该任务
void *user_data; // 任务的参数
struct NJOBS *next; //任务池的下一个任务
struct NJOBS *prev; // 任务池上一个任务
} nJobs;
线程池的创建
int ThreadPoolCreate(threadpool *pool, int numsThread)
{
//1. 初始化线程池相关属性
//2. 创建一定数量线程,扔进线程池
if (pool == NULL)
{
printf("create error\n");
return 1;
}
if (numsThread <= 0)
numsThread = 1;
memset(pool, 0, sizeof(nManager));
pool->totWorkers = 0;
pool->idleWorkers = 0;
pthread_mutex_init(&pool->pool_mtx, NULL);
pthread_cond_init(&pool->pool_cond, NULL);
//pool->pool_mtx = PTHREAD_MUTEX_INITIALIZER;
pthread_t managerpid;
pthread_create(&managerpid, NULL, ManagerThread, (void *)pool);
pthread_detach(managerpid);
int i = 0;
for (i = 0; i < numsThread; i++)
{
nWorker *worker = (nWorker *)malloc(sizeof(nWorker));
if (worker == NULL)
{
printf("worker error\n");
return 1;
}
memset(worker, 0, sizeof(nWorker));
int ret = pthread_create(&worker->pthreadid, NULL, ThreadWorking, worker);
if (ret)
{
perror("pthread create");
return 1;
}
pthread_detach(worker->pthreadid);
worker->terminate = 0;
worker->pool = pool;
LIST_ADD(worker, pool->workers); // 扔进线程池
pthread_mutex_lock(&pool->pool_mtx);
pool->totWorkers++; // 总共线程增加
pool->idleWorkers++; // 空闲线程
pthread_mutex_unlock(&pool->pool_mtx);
}
return 0;
}
销毁线程池
void ThreadPoolDestroy(threadpool *pool)
{
nWorker *w = pool->workers;
for (w = pool->workers; w != NULL; w = w->next)
{
w->terminate = 1; // 令teminate = 1 ,在线程的执行函数中,会自己把自己给毁灭,耦合度低。
}
pthread_mutex_lock(&pool->pool_mtx);
pthread_cond_broadcast(&pool->pool_cond); // 通知所有等待条件锁的线程,没有等待的线程执行完任务之后,会自行销毁
pthread_mutex_unlock(&pool->pool_mtx);
free(pool);
}
线程池添加任务
void ThreadPushJob(threadpool *pool, nJobs *job)
{
pthread_mutex_lock(&pool->pool_mtx);
LIST_ADD(job, pool->jobs); // 向线程池添加任务
printf("add jobs in pool\n");
pthread_cond_signal(&pool->pool_cond); // 通知一个线程来任务了
pthread_mutex_unlock(&pool->pool_mtx);
}
线程执行函数
void *ThreadWorking(void *arg)
{
nWorker *worker = (nWorker *)arg;
while (1)
{
pthread_mutex_lock(&worker->pool->pool_mtx);
while (worker->pool->jobs == NULL)
{
printf("work waiting------\n");
if (worker->terminate == 1)
{
//该线程被销毁
break;
}
pthread_cond_wait(&worker->pool->pool_cond, &worker->pool->pool_mtx); //等到任务池有任务 ,
//在这个函数之前解锁,然后阻塞在该条件变量上,该函数之后加上相应的互斥锁
}
if (worker->terminate == 1)
{
pthread_mutex_unlock(&worker->pool->pool_mtx);
break;
}
nJobs *jobs = worker->pool->jobs;
LIST_DEL(jobs, worker->pool->jobs); //把该任务从任务池取下来
pthread_mutex_unlock(&worker->pool->pool_mtx); // 现在允许其他线程来任务池取任务啦
pthread_mutex_lock(&worker->pool->pool_mtx);
worker->pool->idleWorkers--; // 空闲线程减一
pthread_mutex_unlock(&worker->pool->pool_mtx);
printf("working -------\n");
jobs->func(jobs->user_data); //执行该任务的回调函数
pthread_mutex_lock(&worker->pool->pool_mtx);
worker->pool->idleWorkers++; //空闲线程加一
pthread_mutex_unlock(&worker->pool->pool_mtx);
free(jobs); // 释放该任务内存
}
// 销毁该线程,同时修改与该线程相关的信息
pthread_mutex_lock(&worker->pool->pool_mtx);
worker->pool->totWorkers--;
worker->pool->idleWorkers--;
LIST_DEL(worker, worker->pool->workers); // 从线程池中取出
pthread_mutex_unlock(&worker->pool->pool_mtx);
free(worker); // 释放
pthread_exit(NULL);
}
剩下问题
- 目前线程池不支持,动态的缩小和扩张。一般空闲线程过多的时候,我们考虑减少线程池容量,忙线程多的时候,我们考虑增加线程。
- 在线程池的结构体中已经有了总共线程个数和休闲线程个数,可以使用这个两个进行编写相应程序。
- 线程池大小问题,我们可以使用一个线程:管理者线程来维护线程池的大小.