线程池与性能分析

一.线程池介绍

1.线程池的作用:线程的一种使用模式,线程过多会带来调度开销,进而影响缓存局部性和整体性。而线程池维护着多个线程,等待监督管理者分配可并行执行的任务。这样避免了在短时间内创建和销毁线程的代价。线程池不仅能够内核的充分利用,还能防止过分调度。可用的线程数据取决于可用的并发处理器,处理内核,内存,网络sockets等数量。

2.线程池的作用

(1).复用线程资源;

(2).减少线程创建和销毁的开销

(3).异步处理生产者线程的任务

(4)并发执行:充分利用多核,并发执行核心业务 

3.线程池线程数量如何选择

(1)依据:充分利用系统资源

(2)cpu密集型:   cpu核心数

(3)io密集型:(线程等待时间+cpu运算时间) * cpu核心数/cpu运算时间

二.线程池实现

1.接口设计 :封装原则 隐藏实现细节,暴露使用接口。用户可调用的三个接口

(1).创建线程池:传入参数为线程池数量

(2).销毁线程池:线程池退出,休眠线程先唤醒后退出,执行任务的线程完成当前任务退出

(3).抛出异步任务的接口:构建队列、放入队列、唤醒一个休眠的线程

2.数据结构设计

(1).线程池设计

struct thrdpool_s {
    task_queue_t *task_queue;//任务队列,阻塞在队列中实现
    atomic_int quit;
    int thrd_count;
    pthread_t *threads;
};

(2).任务事件

typedef struct task_s {
    void *next;
    handler_pt func;
    void *arg;
} task_t;

(3).任务队列结构

typedef struct task_queue_s {
    void *head;
    void **tail; //指向最后一个任务的next指针
    int block;
    spinlock_t lock;
    pthread_mutex_t mutex;
    pthread_cond_t cond;
} task_queue_t;

三.接口实现:

1.创建和销毁线程池:使用回滚式编程,在成功后处理完数据逐级销毁资源

thrdpool_t *thrdpool_create(int thrd_count) {
    thrdpool_t *pool;

    pool = (thrdpool_t*)malloc(sizeof(*pool));
    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;
}

void thrdpool_terminate(thrdpool_t * pool) {
    atomic_store(&pool->quit, 1);
    __nonblock(pool->task_queue);
}

2.抛异步任务给线程池

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

3.创建和销毁任务队列:

(1).创建任务队列:首先从堆上分配一个任务队列的内存,还是使用回滚式编程,分配成功就free,然后进行互斥锁和条件变量的初始化,互斥锁初始化成功就destroy,然后进行条件变量初始化。

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 = NULL;
                queue->lock = 1;
                return queue;
            }
            pthread_mutex_destroy(&queue->mutex);
        }

        free(queue);
    }
    return NULL;
}

(2).销毁任务队列:首先释放队列任务分配的资源,销毁原子锁、条件变量、互斥锁,最后free队列

//销毁任务队列,
static void __taskqueue_destroy(task_queue_t *queue){
    task_t *task;
    while(task = __pop_task(&queue)){
        free(task);
    }

    spinlock_destroy(&queue->lock);
    pthread_cond_destroy(&queue->cond);
    pthread_mutex_destroy(&queue->mutex);
    free(queue);
}

4.任务队列添加任务和任务出队:

//添加任务
static inline void __add_task(task_queue_t *queue, void *task){
    //不限定任务类型, 只要该任务的结构其实内存是一个用于链接下一个节点的指针
    void **link = (void**)task;
    *link = NULL;

    spinlock_lock(&queue->lock);
    *queue->tail = link;   /*等价于 queue->tail->next * = link*/
    queue->tail = link;
    spinlock_unlock(&queue->lock);
    pthread_cond_signal(&queue->cond);
}

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;
    task = queue->head;

    void **link = (void**)task;  //link指向task起始位置
    queue->head = *link;    //*link指向task->next

    if(queue->head == NULL){
        queue->tail = &queue->head;
    }
    spinlock_unlock(queue->lock);
    return task;
}

5.获取任务和线程池工作:

static inline void* __get_task(task_queue_t *queue){
    task_t *task;

    //虚假唤醒
    while((task == __pop_task(queue)) == NULL){
        pthread_mutex_lock(&queue->mutex);
        if(queue->block == 0){
            pthread_mutex_unlock(&queue->mutex);
            return NULL;
        }
        //1.先unlock(&mtx)
        //2.在cond 休眠
        //3.--- __add_task 发送signal唤醒
        //3.在cond唤醒
        //4.加上lock(%mtx)

        pthread_cond_wait(&queue->cond, &queue->mutex);
        pthread_mutex_unlock(&queue->mutex);
    }
    return task;
}

static void *__thrdpool_worker(void *arg){
    thrdpool_t *pool = (thrdpool_t*)arg;
    task_t *task;
    void *ctx;

    while(atomic_load(&pool->quit == 0)){
        task = (task_t *)__get_task(pool->task_queue);
        if(!task) break;
        handler_pt func = task->func;
        ctx = task->arg;
        free(task);
        func(ctx);
    }

    return NULL;
}

while循环解决虚假唤醒问题:可能被系统唤醒或者signal唤醒,当队列为空再进入循环再次休眠

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值