异步多任务组件-线程池实现
1 前言
线程池作为作为开发过程中的利器,具有的优势也非常明显:包括减少线程的创建和销毁;异步操作的解耦;将耗时的操作交给线程操作等等。这里主要是梳理线程的原理和关键API的实现。
2 线程池的工作原理
2.1 结构体定义
线程的实现过程比较简单主要包括两个对象,一个是需要被执行的任务task;另一个是执行任务的worker;线程池就是将多个线程和多个任务进行管理的一个角色。利用信号量进行事件通知,利用mutex防止任务抢占问题。
woker作为任务消化者,他就是一个子线程有自己的线程id,线程池启动后,将启动子线程,生命周期由terminate进行管理,当terminate置为1时终止线程。否则会一直从任务队列取任务执行。woker利用队列进行组织。
task作为任务本身,也是直接由外界提供的task函数进行,也就是任务本身,然后任务执行会携带需要处理的用户参数由user_data保存,用户添加任务,将被加入打task队列中。
2.2 线程池处理流程
线程池的处理流程是一个生产消费者的模式,task队列负责生产任务,woker队列负责消费任务;woker之间是相互竞争的关系,一个任务可能被几个woker同时抢夺,但是最终只有一个woker获取到执行权,进行执行。
3 线程池实现
3.1 线程池创建
线程池创建过程,主要做几件事情:首先是初始化信号量和互斥锁;第二个是线程的创建,利用外界传入的线程数进行创建;第三是将这些线程加入到woker队列进行管理。
int thread_pool_create(threadpool *pool, int num_thread) {
if(pool == NULL) return -1;
if(num_thread < 1) num_thread = 1;
memset(pool, 0, sizeof(threadpool));
pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER;
memcpy(&pool->cond, &blank_cond, sizeof(pthread_cond_t));
pthread_mutex_t blank_mtx = PTHREAD_MUTEX_INITIALIZER;
memcpy(&pool->mtx, &blank_mtx, sizeof(pthread_mutex_t));
int idx = 0;
for(idx = 0; idx < num_thread; idx++) {
worker *w = (worker *)malloc(sizeof(worker));
if(w == NULL) {
perror("malloc");
return idx;
}
memset(w, 0, sizeof(worker));
w->pool = pool;
int ret = pthread_create(&w->id, NULL, thread_callback, w);
if(ret) {
perror("pthread_create");
free(w);
return idx;
}
LL_ADD(w, pool->workers);
}
return idx;
}
3.2 线程池销毁
线程销毁是一个反向操作,主要是释放线程和任务队列。
int thread_pool_destory(threadpool *pool, int num_thread) {
worker *w = NULL;
for(w = pool->workers; w != NULL; w = w->next) {
w->terminate = 1;
}
pthread_mutex_lock(&pool->mtx);
pthread_cond_broadcast(&pool->cond);
pthread_mutex_unlock(&pool->mtx);
}
3.3 线程池添加任务
加入任务到任务队列之后需要通知woker进行处理。整个任务队列操作都需要进行加锁保护。
int thread_pool_push_job(threadpool *pool, job *j) {
pthread_mutex_lock(&pool->mtx);
LL_ADD(j, pool->wait_jobs);
pthread_cond_signal(&pool->cond);
pthread_mutex_unlock(&pool->mtx);
}
3.4 线程池woker处理函数
woker处理函数,是线程实体,主要任务就是将任务队列的任务取出,然后调用任务处理函数执行。这里有一个注意点就是当任务队列为空时需要进入等待状态指导有任务触发。
void *thread_callback(void *arg) {
worker *w = (worker *)arg;
while(1) {
pthread_mutex_lock(&w->pool->mtx);
while(w->pool->wait_jobs == NULL) {
if(w->terminate) break;
pthread_cond_wait(&w->pool->cond, &w->pool->mtx);
}
if(w->terminate) {
pthread_mutex_unlock(&w->pool->mtx);
break;
}
job *j = w->pool->wait_jobs;
if(j) {
LL_REMOVE(j, w->pool->wait_jobs);
}
pthread_mutex_unlock(&w->pool->mtx);
if(j == NULL) continue;
j->job_func(j);
}
free(w);
}
3.5 队列操作函数
队列操作主要是进行队列的添加和删除操作。加入队列采用头插法。
#define LL_ADD(item, list) do { \
item->prev = NULL; \
item->next = list; \
if(list != NULL) list->prev = item; \
list = item; \
} while(0)
#define LL_REMOVE(item, list) do { \
if(item->prev != NULL) item->prev->next = item->next; \
if(item->next != NULL) item->next->prev = item->prev; \
if(list == item) list = item->next; \
item->prev = item->next = NULL; \
} while(0)