C 线程池的实现以及相关问题

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

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值