C语言实现线程池组件

        基础概念请参照这篇文章https://blog.csdn.net/qw_6918966011/article/details/132124331?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522B04F3061-61D7-42F8-825D-C800ED151674%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=B04F3061-61D7-42F8-825D-C800ED151674&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-2-132124331-null-null.142^v100^pc_search_result_base8&utm_term=c%E8%AF%AD%E8%A8%80%E5%AE%9E%E7%8E%B0%E7%BA%BF%E7%A8%8B%E6%B1%A0&spm=1018.2226.3001.4187
        
        线程池实现流程:
        

//数据结构的实现

//任务节点
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;//存储线程池中的标识符
}

//创建任务队列并返回指向它的指针。

static task_queue_t *
__taskqueue_create() {
    int ret;
    //sizeof(*queue) 也可以
    task_queue_t *queue = (task_queue_t *)malloc(sizeof(task_queue_t));
    if (queue) {
        //初始化了队列的互斥锁 mutex。如果初始化成功(返回值为0
        ret = pthread_mutex_init(&queue->mutex, NULL);
        if (ret == 0) {
            //初始化了条件变量 cond。如果初始化成功(返回值为0)
            ret = pthread_cond_init(&queue->cond, NULL);
            if (ret == 0) {
                //调用 spinlock_init() 初始化了自旋锁 lock
                spinlock_init(&queue->lock);
                queue->head = NULL;
                queue->tail = &queue->head;
                queue->block = 1;//将标志位 block 设置为1(表示在没有任务时阻塞等待)
                return queue;
            }
            pthread_mutex_destroy(&queue->mutex);
        }
        free(queue);
    }
    return NULL;
}

//设置任务队列为非阻塞模式
static void
__nonblock(task_queue_t *queue) {
    //获取了队列的互斥锁 mutex。确保在更新队列状态时不会被其他线程干扰。
    pthread_mutex_lock(&queue->mutex);
    //将队列的标志位 block 设置为0,表示将任务队列切换到非阻塞模式
    queue->block = 0;
    //调用 pthread_mutex_unlock() 释放互斥锁
    pthread_mutex_unlock(&queue->mutex);
    //调用 pthread_cond_broadcast() 广播条件变量 cond,通知等待该条件的所有线程有新的状态变化
    pthread_cond_broadcast(&queue->cond);
}

//往任务队列中添加任务
static inline void 
__add_task(task_queue_t *queue, void *task) {
    // 不限定任务类型,只要该任务的结构起始内存是一个用于链接下一个节点的指针
    void **link = (void**)task;
    *link = NULL;
    //调用 spinlock_lock() 获取自旋锁 lock,确保在更新队列状态时不会被其他线程干扰。
    spinlock_lock(&queue->lock);
    //将队列尾部的指针(即上一个节点的 next 指针)设置为 link,实现将新任务链接到队列末尾。
    *queue->tail /* 等价于 queue->tail->next */ = link;
    //更新队列的尾部指针为当前插入的任务 link
    queue->tail = link;
    //调用 spinlock_unlock() 释放自旋锁
    spinlock_unlock(&queue->lock);
    //调用 pthread_cond_signal() 发送信号给等待该条件变量的线程之一。这样可以通知有新任务加入队列,并唤醒等待中的线程来处理新任务。
    pthread_cond_signal(&queue->cond);
}


//从任务队列中弹出一个任务
static inline void * 
__pop_task(task_queue_t *queue) {
    //调用 spinlock_lock() 获取自旋锁 lock,确保在访问和更新队列状态时不会被其他线程干扰
    spinlock_lock(&queue->lock);
    if (queue->head == NULL) {
        //检查队列的头部指针 head 是否为 NULL。如果是空队列,则释放自旋锁并返回 NULL,表示没有可弹出的任务。
        spinlock_unlock(&queue->lock);
        return NULL;
    }
    //将队列的头部指针赋值给变量 task,表示要弹出的任务
    task_t *task;
    task = queue->head;

    //将任务结构转换为指向指针的指针 link
    void **link = (void**)task;
    //队列的头部指针更新为当前任务的 next 指针所指向的位置。这样就实现了从队列中移除一个任务。
    queue->head = *link;

    //检查更新后的头部指针是否为 NULL。如果是空队列,则将尾部指针重新设置为指向头部指针(即尾部与头部相等)
    if (queue->head == NULL) {
        queue->tail = &queue->head;
    }

    //通过调用 spinlock_unlock() 释放自旋锁,并返回之前保存的待弹出的任务指针
    spinlock_unlock(&queue->lock);
    return task;
}
//从任务队列中获取一个任务
static inline void * 
__get_task(task_queue_t *queue) {
    task_t *task;
    // 虚假唤醒
    //调用 __pop_task(queue) 函数从队列中弹出一个任务并将其赋值给 task 变量。如果弹出的结果为 NULL,说明当前队列为空
    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 休眠
        // --- __add_task 时唤醒
        // 3. 在 cond 唤醒
        // 4. 加上 lock(&mtx);
        pthread_cond_wait(&queue->cond, &queue->mutex);
        pthread_mutex_unlock(&queue->mutex);
    }
    return task;
}


//销毁任务队列的函数
static void
__taskqueue_destroy(task_queue_t *queue) {
    task_t *task;
    while ((task = __pop_task(queue))) {
        free(task);
    }
    //调用 spinlock_destroy(&queue->lock) 来销毁自旋锁对象 queue->lock
    spinlock_destroy(&queue->lock);
    //调用 pthread_cond_destroy(&queue->cond) 来销毁条件变量对象 queue->cond
    pthread_cond_destroy(&queue->cond);
    //调用 pthread_mutex_destroy(&queue->mutex) 来销毁互斥锁对象 queue->mutex
    pthread_mutex_destroy(&queue->mutex);
    //调用 free(queue) 函数释放任务队列结构体本身所占用的内存资源
    free(queue);
}

//给线程池创建固定数量的线程
static int 
__threads_create(thrdpool_t *pool, size_t thrd_count) {
    pthread_attr_t attr;
	int ret;

    ret = pthread_attr_init(&attr);

    if (ret == 0) {
        pool->threads = (pthread_t *)malloc(sizeof(pthread_t) * thrd_count);
        if (pool->threads) {
            int i = 0;
            for (; i < thrd_count; i++) {
                if (pthread_create(&pool->threads[i], &attr, __thrdpool_worker, pool) != 0) {
                    break;
                }
            }
            pool->thrd_count = i;
            pthread_attr_destroy(&attr);
            if (i == thrd_count)
                return 0;
            __threads_terminate(pool);
            free(pool->threads);
        }
        ret = -1;
    }
    return ret; 
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值