1.如何实现线程池的?
- - 使用互斥锁保证对线程池的互斥访问,使用条件变量实现同步。
- - 初始化线程池,创建worker线程。
- - 各worker最外层为while循环,获得互斥锁的线程可以进入线程池,若无task队列为空则 pthread_cond_wait自动解锁互斥量,置该线程为等待状态并等待条件触发。若存在task则取出队列第一个任务,**之后立即开锁,之后再并执行具体操作**。这里若先执行后开锁则在task完成前整个线程池处于锁定状态,其他线程不能取任务,相当于串行操作!
- - 建立连接后,当客户端请求到达服务器端,创建task任务并添加到线程池task队列尾,当添加完task之后调用pthread_cond_signal唤醒因没有具体task而处于等待状态的worker线程。
2. 如何实现线程池同步互斥?
处理线程同步互斥问题可以考虑互斥锁、条件变量、读写锁和信号量。本线程池为1:N模型,主线程负责监听并负责添加任务(建立连接 + 创建参数)到线程池中,之后worker线程负责取任务并执行,可供选择的同步策略可以是"互斥锁 + 条件变量"或信号量来完成。
- 互斥锁 + 条件变量(线程同步)
int pthread_mutex_lock(pthread_mutex_t *mptr);
int pthread_mutex_unlock(pthread_mutex_t *mptr);
int pthread_cond_wait(pthread_cond_t *cptr, pthread_mutex_t *mptr);
int pthread_cond_signal(pthread_cond_t *cptr);
- 信号量(进程、线程同步)
int sem_init(sem_t *sem, int shared, unsigned int value);
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);
int sem_destory(sem_t *sem);
其实信号量初值设为1(二元信号量)时,可以实现互斥锁功能,信号量初值为N时可以实现条件变量功能。不过信号量主要上锁和解锁可以在不同线程,同步操作容易写错,另外,信号量必须先处理同步信号量再用互斥信号量包住临界区,这里写错会发生死锁情况。所以本线程池使用互斥锁 + 条件变量来实现。
线程池的数据成员
typedef struct tk_task{
void (*func)(void*); // 回调函数指针
void* arg; // 回调函数参数
struct tk_task* next; // 任务链表(下一节点指针)
}tk_task_t;
typedef struct threadpool{
pthread_mutex_t lock; // 互斥锁
pthread_cond_t cond; // 条件变量
pthread_t *threads; // 线程
tk_task_t *head; // 任务链表
int thread_count; // 线程数
int queue_size; // 任务链表长
int shutdown; // 关机模式
int started;
}tk_threadpool_t;
线程池的函数
tk_threadpool_t* threadpool_init(int thread_num);
int threadpool_add(tk_threadpool_t* pool, void (*func)(void *), void* arg);
int threadpool_destroy(tk_threadpool_t* pool, int gracegul);
初始化线程池,线程池包括任务队列和线程数组两部分,这两部分内存动态分配出来
// 初始化线程池
tk_threadpool_t *threadpool_init(int thread_num){
// 分配线程池
tk_threadpool_t* pool;
if((pool = (tk_threadpool_t *)malloc(sizeof(tk_threadpool_t))) == NULL)
goto err;
// threads指针指向线程数组(存放tid),数组大小即为线程数
pool->thread_count = 0;
pool->queue_size = 0;
pool->shutdown = 0;
pool->started = 0;
pool->threads = (pthread_t*)malloc(sizeof(pthread_t) * thread_num);
// 分配并初始化task头结点
pool->head = (tk_task_t*)malloc(sizeof(tk_task_t));
if((pool->threads == NULL) || (pool->head == NULL))
goto err;
pool->head->func = NULL;
pool->head->arg = NULL;
pool->head->next = NULL;
// 初始化锁
if(pthread_mutex_init(&(pool->lock), NULL) != 0)
goto err;
// 初始化条件变量
if(pthread_cond_init(&(pool->cond), NULL) != 0)
goto err;
// 创建线程
for(int i = 0; i < thread_num; ++i){
if(pthread_create(&(pool->threads[i]), NULL, threadpool_worker, (void*)pool) != 0){
threadpool_destory(pool, 0);
return NULL;
}
pool->thread_count++;
pool->started++;
}
return pool;
err:
if(pool)
threadpool_free(pool);
return NULL;
}
线程绑定函数
void *threadpool_worker(void *arg){
if(arg == NULL)
return NULL;
tk_threadpool_t *pool = (tk_threadpool_t *)arg;
tk_task_t *task;
while(1){
// 对线程池上锁
pthread_mutex_lock(&(pool->lock));
// 没有task且未停机则阻塞
while((pool->queue_size == 0) && !(pool->shutdown))
pthread_cond_wait(&(pool->cond), &(pool->lock));
// 立即停机模式、平滑停机且没有未完成任务则退出
if(pool->shutdown == immediate_shutdown)
break;
else if((pool->shutdown == graceful_shutdown) && (pool->queue_size == 0))
break;
// 得到第一个task
task = pool->head->next;
// 没有task则开锁并进行下一次循环
if(task == NULL){
pthread_mutex_unlock(&(pool->lock));
continue;
}
// 存在task则取走并开锁
pool->head->next = task->next;
pool->queue_size--;
pthread_mutex_unlock(&(pool->lock));
// 设置task中func参数
(*(task->func))(task->arg);
free(task);
}
pool->started--;
pthread_mutex_unlock(&(pool->lock));
pthread_exit(NULL);
return NULL;
}
像任务队列中添加任务
int threadpool_add(tk_threadpool_t* pool, void (*func)(void *), void *arg){
int rc, err = 0;
if(pool == NULL || func == NULL)
return -1;
if(pthread_mutex_lock(&(pool->lock)) != 0)
return -1;
// 已设置关机
if(pool->shutdown){
err = tk_tp_already_shutdown;
goto out;
}
// 新建task并注册信息
tk_task_t *task = (tk_task_t *)malloc(sizeof(tk_task_t));
if(task == NULL)
goto out;
task->func = func;
task->arg = arg;
// 新task节点在head处插入
task->next = pool->head->next;
pool->head->next = task;
pool->queue_size++;
rc = pthread_cond_signal(&(pool->cond));
out:
if(pthread_mutex_unlock(&pool->lock) != 0)
return -1;
return err;
}
释放线程池资源
// 释放线程资源
int threadpool_destory(tk_threadpool_t *pool, int graceful){
if(pool == NULL)
return tk_tp_invalid;
if(pthread_mutex_lock(&(pool->lock)) != 0)
return tk_tp_lock_fail;
int err = 0;
do{
if(pool->shutdown){
err = tk_tp_already_shutdown;
break;
}
pool->shutdown = (graceful) ? graceful_shutdown : immediate_shutdown;
if(pthread_cond_broadcast(&(pool->cond)) != 0){
err = tk_tp_cond_broadcast;
break;
}
if(pthread_mutex_unlock(&(pool->lock)) != 0){
err = tk_tp_lock_fail;
break;
}
// 回收每个线程资源
for(int i = 0; i < pool->thread_count; i++){
if(pthread_join(pool->threads[i], NULL) != 0){
err = tk_tp_thread_fail;
}
}
}while(0);
if(!err){
pthread_mutex_destroy(&(pool->lock));
pthread_cond_destroy(&(pool->cond));
threadpool_free(pool);
}
return err;
}
两个辅助函数
static int threadpool_free(tk_threadpool_t *pool);
static void* threadpool_worker(void *arg);
// 释放线程池
int threadpool_free(tk_threadpool_t *pool){
if(pool == NULL || pool->started > 0)
return -1;
// 释放线程数组
if(pool->threads)
free(pool->threads);
// 逐节点销毁task链表
tk_task_t *old;
while(pool->head->next){
old = pool->head->next;
pool->head->next = pool->head->next->next;
free(old);
}
return 0;
}