服务器并发之线程池

概要

  • 线程池的好处
  • 线程池的组成
  • 线程池中每个线程是如何实现同步的
  • 任务放置在任务队列是如实现的

线程池的好处

当只使用1种线程模式,线程过多会带来调度的开销(主要为创建线程与销毁线程以及CPU时间轮转切换的开销),进而导致了缓存的局部性和整体性。而线程池维护者多个线程,等待监督管理者分配可执行的任务,管理者只需要设置其回调函数就可以让其自动执行。这样避免了在短时间内创建和销毁线程的代价。线程池不仅能够让内核充分的利用还能防止过度的调用。在线程池中设置线程的数量是由可用的并发处理器,处理内核,内存,网络socket等数量。

线程池的组成

  • 线程池管理器,包括如下

    1.互斥量
    2. 任务队列
    3. 环境变量
    4. 线程数量
    5. 最大任务队列

struct thread_pool_s {
    pthread_mutex_t        mtx;
    thread_pool_queue_t   queue;
    int_t                 waiting;
    pthread_cond_t         cond;

    char                  *name;
    uint_t                threads;
    int_t                 max_queue;
};

线程池管理器如图
在这里插入图片描述

  • 任务节点

    1.指向下一个任务节点
    2. 当前任务标号
    3. 任务的上下文
    4. 任务的处理函数

struct thread_task_s {
    thread_task_t       *next;
    uint_t               id;
    void                *ctx;
    void               (*handler)(void *data);
};
  • 线程池任务队列
    1.永远指向任务队列的队头
    2. 永远指向任务队列的队尾
typedef struct {
    thread_task_t        *first;
    thread_task_t        **last;
} thread_pool_queue_t;

任务队列如图
在这里插入图片描述

线程池中每个线程是如何实现同步的/任务放置

实现线程同步的方式有许多比如

  • 互斥量pthread_mutex_
  • 读写锁pthread_rwlock_
  • 条件变量pthread_cond_
  • 信号量sem_
    本文这里使用pthread_mutex
thread_mutex_lock(&tp->mtx)

每个线程应该随时监听任务队列是否有任务(线程池是一种争夺式的工作模式),也就是监管者在放置任务到任务队列的时候应该通知线程池里面的线程。

thread_cond_signal(&tp->cond)

具体代码如下

int_t
thread_task_post(thread_pool_t *tp, thread_task_t *task)
{
    if (thread_mutex_lock(&tp->mtx) != OK) {
        return ERROR;
    }

    if (tp->waiting >= tp->max_queue) {
        (void) thread_mutex_unlock(&tp->mtx);

        fprintf(stderr,"thread pool \"%s\" queue overflow: %ld tasks waiting\n",
                      tp->name, tp->waiting);
        return ERROR;
    }

    //task->event.active = 1;

    task->id = thread_pool_task_id++;
    task->next = NULL;

    if (thread_cond_signal(&tp->cond) != OK) {
        (void) thread_mutex_unlock(&tp->mtx);
        return ERROR;
    }

    *tp->queue.last = task; // 尾插法,插入最后一个节点的后面
    tp->queue.last = &task->next; //last始终指向任务链表的最后一个节点

    tp->waiting++;

    (void) thread_mutex_unlock(&tp->mtx);

    if(debug)fprintf(stderr,"task #%lu added to thread pool \"%s\"\n",
                   task->id, tp->name);

    return OK;
}

当任务队列没有任务需要做时,此时应该让出cpu,同时释放互斥量。

thread_cond_wait(&tp->cond, &tp->mtx)

具体代码如下

thread_pool_t *tp = data;

    int                 err;
    thread_task_t       *task;


    if(debug)fprintf(stderr,"thread in pool \"%s\" started\n", tp->name);

   

    for ( ;; ) {
        if (thread_mutex_lock(&tp->mtx) != OK) {
            return NULL;
        }

        
        tp->waiting--;
        /*
        阻塞等待条件变量cond
        释放已掌握的互斥锁(解锁互斥量)相当于ptherad_mutex_unlock(&mutex);
        当被唤醒,pthread_cond_wait函数返回时,解除阻塞并重新申请获取互斥锁pthread_mutex_lock(&mutex);
        */
        while (tp->queue.first == NULL) { //判断任务链表中是否有任务
            if (thread_cond_wait(&tp->cond, &tp->mtx)
                != OK)
            {
                (void) thread_mutex_unlock(&tp->mtx);
                return NULL;
            }
        }

        task = tp->queue.first;
        tp->queue.first = task->next;

        if (tp->queue.first == NULL) {
            tp->queue.last = &tp->queue.first;
        }
		
        if (thread_mutex_unlock(&tp->mtx) != OK) {
            return NULL;
        }



        if(debug) fprintf(stderr,"run task #%lu in thread pool \"%s\"\n",
                       task->id, tp->name);

        task->handler(task->ctx);

        if(debug) fprintf(stderr,"complete task #%lu in thread pool \"%s\"\n",task->id, tp->name);

        task->next = NULL;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值