LinuxC/C++ 实现简易线程池

14 篇文章 2 订阅

LinuxC/C++ 实现简易线程池



实现原理

线程池是一种池化的技术,类似的还有数据库连接池、HTTP 连接池等等.
池化的思想主要是为了减少每次获取和结束资源的消耗,提高对资源的利用率.

线程池的原理类似于生产者和消费者模型,线程池中的线程是消费者,任务队列中的任务则是生产者生产的消费品,当任务队列存在多个任务时,便会由调度器依次将任务派发给现有的线程执行.

实现一个线程池主要有三大部分:

  • 任务队列
  • 执行队列
  • 管理组件(锁 + 条件变量)

在这里插入图片描述

实现线程池

头文件

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

定义结构体

我们首先定义以下三个结构体:任务节点、员工节点、管理者节点(线程池).

// 任务结构体
struct nTask {
    void (*task_func)(struct nTask *task);   // 存储待执行的任务
    void *user_data;                // 任务参数
    struct nManager *pool;

    struct nTask *prev;
    struct nTask *next;
};

// 执行结构体
struct nWorker {
    pthread_t threadid;
    int terminate;
    struct nManager *pool;

    struct nWorker *prev;
    struct nWorker *next;
};

// 管理组件
typedef struct nManager {
    struct nTask *tasks;
    struct nWorker *workers;

    pthread_mutex_t mutex;
    pthread_cond_t cond;        // 条件变量
} ThreadPool;

操作队列的公共方法

这里要注意,因为线程池结构体字段中的任务队列和执行者队列都是指针类型,所以下列函数传入的参数必须是二级指针(原来写的是一级指针,找了很久的错误).
为了可扩展性,这里建议写成泛型.

void TASK_INSERT(struct nTask **item, struct nTask **tasks) {
    if (*tasks == NULL) {
        *tasks = *item;
        (*tasks)->next = (*tasks)->prev = NULL;
    } else {
        (*item)->prev = NULL;
        (*item)->next = *tasks;
        (*tasks)->prev = *item;
        *tasks = *item;
    }
}
void TASK_REMOVE(struct nTask **item, struct nTask **tasks) {
    if ((*tasks)->next == NULL) {
        *item = *tasks;
        *tasks = NULL;
    } else {
        *item = *tasks;
        *tasks = (*tasks)->next;
        (*tasks)->prev = NULL;
    }
}
void WORKER_INSERT(struct nWorker **item, struct nWorker **workers) {
    if (*workers == NULL) {
        *workers = *item;
        (*workers)->next = (*workers)->prev = NULL;
    } else {
        (*item)->prev = NULL;
        (*item)->next = *workers;
        (*workers)->prev = *item;
        *workers = *item;
    }
}

创建并初始化线程池

int nThreadPoolCreate(ThreadPool *pool, int numWorker) {
    if (pool == NULL) return -1;
    if (numWorker < 1) numWorker = 1;
    // 先把pool初始化为空
    memset(pool, 0, sizeof(ThreadPool));

    // 初始化条件变量
    pthread_cond_init(&pool->cond, NULL);
    // pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER;
    // memcpy(&pool->cond, &blank_cond, sizeof(pthread_cond_t));

    // 初始化互斥锁
    pthread_mutex_init(&pool->mutex, NULL);
    // pthread_mutex_t blank_mutex = PTHREAD_MUTEX_INITIALIZER;
    // memcpy(&pool->mutex, &blank_mutex, sizeof(pthread_mutex_t));

    printf("nThreadPoolCreate\n");
    int i = 0;

    for (i = 0; i < numWorker; i++) {
        struct nWorker *worker = (struct nWorker *)malloc(sizeof(struct nWorker));
        if (worker == NULL) {
            perror("malloc");
            return -2;
        }
        // 置为0
        memset(worker, 0, sizeof(struct nWorker));
        // 员工可以访问线程池
        worker->pool = pool;
        worker->threadid = i;
        worker->terminate = 0;
        
        // 创建线程
        int ret = pthread_create(&(worker->threadid), NULL, nThreadPoolCallback, worker);
        // 创建失败返回非0
        if (ret) {
            perror("pthread_create");
            free(worker);
            worker = NULL;
            return -3;
        }
        WORKER_INSERT(&worker, &(pool->workers));
    }
    // 成功
    return 0;
}

销毁线程池

int nThreadPoolDestory(ThreadPool *pool, int nWorker) {
    // 把每一个worker的terminate置为1即可
    struct nWorker *worker = NULL;
    for (worker = pool->workers; worker != NULL; worker = worker->next) {
        worker->terminate = 1;
    }

    // 做一个广播,让处于等待状态的线程不再处于等待
    // 因为线程处于等待状态的时候也使用了这把锁,所以为了防止后面线程等待再来抢这把锁而,这里需要占用锁
    pthread_mutex_lock(&pool->mutex);

    pthread_cond_broadcast(&pool->cond);    // 唤醒所有

    pthread_mutex_unlock(&pool->mutex);

    pool->workers = NULL;
    pool->tasks = NULL;

    return 0;
}

向任务队列添加任务

int nThreadPoolPushTask(ThreadPool *pool, struct nTask *task) {
    if (pool == NULL || task == NULL) {
        return -1;
    }
    pthread_mutex_lock(&pool->mutex);

    TASK_INSERT(&task, &(pool->tasks));

    pthread_cond_signal(&pool->cond);   // 唤醒一个线程

    pthread_mutex_unlock(&pool->mutex);
}

线程回调函数

回调函数传入一个执行者,然后执行者从任务队列中取出一个任务,再执行. 拿营业厅来举例子,线程回调函数就像是员工在受理客户的请求,帮助客户完成业务这一过程.

// 线程回调函数
void *nThreadPoolCallback(void *arg) {
    struct nWorker *worker = (struct nWorker*)arg;
    
    while (1) {
        pthread_mutex_lock(&worker->pool->mutex);
        // 如果任务队列为空,线程处于等待状态
        while (worker->pool->tasks == NULL) {
            // 终止
            if (worker->terminate) {
                break;
            }
            printf("Thread is waiting\n");
            pthread_cond_wait(&worker->pool->cond, &worker->pool->mutex);
        }
        pthread_mutex_unlock(&worker->pool->mutex);
        // 再break掉一层
        if (worker->terminate) {
            // 先解锁,防止引发死锁
            pthread_mutex_unlock(&worker->pool->mutex);
            break;
        }

        // 如果任务队列不为空,就取出一个任务出来(首节点)
        struct nTask *task = worker->pool->tasks;
        if (task) {
            TASK_REMOVE(&task, &(worker->pool->tasks));
        }

        if (task == NULL) {
            continue;
        }
        // 开始处理这个任务
        task->task_func(task);
    }
    if (worker != NULL) {
        free(worker);
    }
    worker = NULL;
}

调用接口

#define THREADPOOL_INIT_COUNT       20
#define TASK_INIT_SIZE              50

// 客户要办理的业务
void task_entry(struct nTask *_task) {
    struct nTask *task = _task;
    int idx = *(int *)task->user_data;

    printf("Execute Task: %d\n", idx);

    free(task->user_data);
    free(task);
}

int main() {
    ThreadPool pool;
    nThreadPoolCreate(&pool, THREADPOOL_INIT_COUNT);

    int i = 0;
    for (i = 0; i < TASK_INIT_SIZE; i++) {
        struct nTask *task = (struct nTask *)malloc(sizeof(struct nTask));
        // 一毫秒创建一个任务
        usleep(1000);
        if (task == NULL) {
            perror("malloc");
            exit(1);
        }
        memset(task, 0, sizeof(struct nTask));

        task->task_func = task_entry;
        task->user_data = malloc(sizeof(int));
        *(int *)task->user_data = i;

        nThreadPoolPushTask(&pool, task);
    }
    sleep(2);
    // 释放线程池
    nThreadPoolDestory(&pool, THREADPOOL_INIT_COUNT);
    printf("\nnThreadPoolDestory\n");
}

调用结果

nThreadPoolCreate
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Thread is waiting
Execute Task: 0
Thread is waiting
Execute Task: 1
Thread is waiting
Execute Task: 2
Thread is waiting
Execute Task: 3
Thread is waiting
Execute Task: 4
Thread is waiting
Execute Task: 5
Thread is waiting
Execute Task: 6
Thread is waiting
Execute Task: 7
Thread is waiting
Execute Task: 8
Thread is waiting
Execute Task: 9
Thread is waiting
Execute Task: 10
Thread is waiting
Execute Task: 11
Thread is waiting
Execute Task: 12
Thread is waiting
Execute Task: 13
Thread is waiting
Execute Task: 14
Thread is waiting
Execute Task: 15
Thread is waiting
Execute Task: 16
Thread is waiting
Execute Task: 17
Thread is waiting
Execute Task: 18
Thread is waiting
Execute Task: 19
Thread is waiting
Execute Task: 20
Thread is waiting
Execute Task: 21
Thread is waiting
Execute Task: 22
Thread is waiting
Execute Task: 23
Thread is waiting
Execute Task: 24
Thread is waiting
Execute Task: 25
Thread is waiting
Execute Task: 26
Thread is waiting
Execute Task: 27
Thread is waiting
Execute Task: 28
Thread is waiting
Execute Task: 29
Thread is waiting
Execute Task: 30
Thread is waiting
Execute Task: 31
Thread is waiting
Execute Task: 32
Thread is waiting
Execute Task: 33
Thread is waiting
Execute Task: 34
Thread is waiting
Execute Task: 35
Thread is waiting
Execute Task: 36
Thread is waiting
Execute Task: 37
Thread is waiting
Execute Task: 38
Thread is waiting
Execute Task: 39
Thread is waiting
Execute Task: 40
Thread is waiting
Execute Task: 41
Thread is waiting
Execute Task: 42
Thread is waiting
Execute Task: 43
Thread is waiting
Execute Task: 44
Thread is waiting
Execute Task: 45
Thread is waiting
Execute Task: 46
Thread is waiting
Execute Task: 47
Thread is waiting
Execute Task: 48
Thread is waiting
Execute Task: 49
Thread is waiting

nThreadPoolDestory

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值