手写线程池关键API

线程池使用场景

线程池是管理一定数量的线程,可以避免频繁的创造和销毁线程。同时,在面对一些耗时任务时,通过线程池可以对耗时任务进行异步处理,提高系统的并发性。

线程池组件

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;
}

分配任务资源,并将任务放入队列中。

  • 8
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值