线程池使用场景
线程池是管理一定数量的线程,可以避免频繁的创造和销毁线程。同时,在面对一些耗时任务时,通过线程池可以对耗时任务进行异步处理,提高系统的并发性。
线程池组件
typedef struct task_s {
void *next;
handler_pt func;
void *arg;
} task_t;
typedef struct task_queue_s {
void *head;
void **tail;
int block;
spinlock_t lock;
pthread_mutex_t mutex;
pthread_cond_t cond;
} task_queue_t;
struct thrdpool_s {
task_queue_t *task_queue;
atomic_int quit;
int thrd_count;
pthread_t *threads;
};
生产-消费者模型中,线程池通过队列管理消费者线程,按照先来后到的顺序分配任务。
队列操作的API
static task_queue_t *
__taskqueue_create() {
int ret;
task_queue_t* queue=(task_queue_t*)malloc(sizeof(task_queue_t));
if(queue){
ret=pthread_mutex_init(&queue->mutex,NULL);
if(ret==0){
ret=pthread_cond_init(&queue->cond,NULL);
if(ret==0){
spinlock_init(&queue->lock);
queue->head=NULL;
queue->tail=&queue->head;
queue->block=1;
return queue;
}
pthread_mutex_destroy(&queue->mutex);
}
free(queue);
}
return NULL;
}
static void
__nonblock(task_queue_t *queue) {
pthread_mutex_lock(&queue->mutex);
queue->block=0;
pthread_mutex_unlock(&queue->mutex);
pthread_cond_broadcast(&queue->cond);
}
创建队列,并将其设置为非阻塞。创建资源时推荐使用回滚式编程,创建不成功需要将之前分配的资源进行释放。
static inline void
__add_task(task_queue_t *queue, void *task) {
/*不限制任务类型,只要该任务结构起始内存是一个next 指针*/
void ** link=(void**)task;
*link=NULL;
spinlock_lock(&queue->lock);
*queue->tail/*相当于queue->tail->next*/=link;
queue->tail=link;
spinlock_unlock(&queue->lock);
pthread_cond_signal(&queue->cond);
}
向队列中加任务,这里可以着重注意队列结构体中的tail指针的处理,它是一个二级指针,而task中首地址是指向下一个任务的void*指针,*link就是next指针,queue->tail就相当于queue->tail->next,这里真的值得细品,建议结合内存模型细细分析,linux源码中对于队列也是这样的操作,同时将next指针设为void类型还可以不对任务类型设限,扩展性更强。
static inline void *
__pop_task(task_queue_t *queue) {
spinlock_lock(&queue->lock);
if(queue->head==NULL){
spinlock_unlock(&queue->lock);
return NULL;
}
task_t *task=queue->head;
queue->head=task->next;
if(queue->head==NULL){
queue->tail=&queue->head;
}
spinlock_unlock(&queue->lock);
return task;
}
static inline void *
__get_task(task_queue_t *queue) {
task_t* task;
/*while可以防止虚假唤醒*/
while((task=__pop_task(queue))==NULL){
pthread_mutex_lock(&queue->mutex);
if(queue->block==0){
pthread_mutex_unlock(&queue->mutex);
return NULL;
}
/*在add_task时唤醒*/
pthread_cond_wait(&queue->cond, &queue->mutex);
pthread_mutex_unlock(&queue->mutex);
}
return task;
}
get调用的是pop,当pop不出任务时,也就是队列中没有任务时,队列会将线程堵塞,直到有任务添加到队列中才会唤醒。此处不能用if,否则会少一次检查,容易造成虚假唤醒。
线程池API
thrdpool_t *
thrdpool_create(int thrd_count) {
thrdpool_t *pool = (thrdpool_t*)malloc(sizeof(thrdpool_t));
if(pool){
task_queue_t *queue = __taskqueue_create();
if(queue){
pool->task_queue=queue;
atomic_init(&pool->quit,0);
if(__threads_create(pool, thrd_count)==0){
return pool;
}
__taskqueue_destroy(queue);
}
free(pool);
}
return NULL;
}
创建线程池,分配资源
int
thrdpool_post(thrdpool_t *pool, handler_pt func, void *arg) {
if(atomic_load(&pool->quit)==1){
return -1;
}
task_t* task=(task_t*)malloc(sizeof(task_t));
if(!task)return -1;
task->func=func;
task->arg=arg;
__add_task(pool->task_queue,task);
return 0;
}
分配任务资源,并将任务放入队列中。