用C语言实现简单线程池——初学者入门教程

线程池是提升多线程程序性能和资源利用率的重要技术。它通过预先创建一定数量的线程,来执行多个任务,避免了频繁创建和销毁线程带来的开销。

本文将带你一步步完成一个简易的C语言线程池实现,适合初学者学习和参考。


线程池设计思路

  1. 任务队列
    线程池内部维护一个任务队列,存放未执行的任务。

  2. 工作线程
    线程池创建多个工作线程,它们不断从任务队列中取任务执行。

  3. 任务结构
    每个任务封装成结构体,包含执行函数和函数参数。

  4. 同步机制
    线程间共享任务队列,需要互斥锁保证数据安全。
    工作线程等待任务时,使用条件变量阻塞等待。


用C语言实现线程池 —— 详细代码讲解版


1. 头文件及其作用

#include <stdio.h>      // 标准输入输出,printf等
#include <stdlib.h>     // 标准库分配内存,malloc/free,exit等
#include <pthread.h>    // POSIX线程库,线程、锁、条件变量相关API
#include <unistd.h>     // sleep函数,模拟延时使用
  • stdio.h:提供输入输出功能,比如printf打印调试信息。

  • stdlib.h:提供动态内存管理(mallocfree)以及其他工具函数。

  • pthread.h:管理线程、互斥锁、条件变量,是多线程编程基础。

  • unistd.h:提供sleep函数,方便用来模拟任务执行耗时,实际生产环境中可去掉。


2. 线程池所需的基本数据结构

2.1 任务结构体 task_t

typedef struct task_t {
    void (*function)(void *);     // 任务执行函数指针,接受void*参数,无返回值
    void *arg;                    // 任务函数的参数,使用void*,泛型指针方便传任意数据
    struct task_t *next;          // 链表指针,方便多个任务串联成队列
} task_t;
  • 任务是什么?
    任务是线程池要执行的工作,比如打印数字、文件处理等。

  • 为什么用函数指针+参数?
    使用void (*function)(void *),让多个不同类型的任务用一个接口表示,任务真正逻辑在具体函数中实现。

  • 链表的作用
    为了维护多个任务,使用链表存储任务队列。


2.2 线程池结构体 threadpool_t

typedef struct threadpool_t {
    pthread_mutex_t lock;          // 互斥锁,保护任务队列,防止数据竞态
    pthread_cond_t notify;         // 条件变量,任务队列为空时工作线程等待,添加任务时通知
    pthread_t *threads;            // 工作线程数组,保存线程ID
    task_t *task_queue_head;       // 任务队列头指针
    task_t *task_queue_tail;       // 任务队列尾指针,加任务时方便添加末尾
    int thread_count;              // 线程池中线程个数
    int shutdown;                  // 标志线程池是否关闭,控制线程循环退出
} threadpool_t;
  • 互斥锁
    多线程同时操作任务队列必须加锁避免混乱。

  • 条件变量
    如果任务队列空,工作线程阻塞等待,有新任务时被唤醒。

  • 线程数组
    线程池创建一定数量线程,用线程数组保存各线程的ID,便于后续pthread_join回收。

  • shutdown标志
    用于安全关闭线程池,控制线程退出循环。


3. 线程池创建函数 threadpool_create

threadpool_t *threadpool_create(int thread_count);
  • 参数
    传入线程数,用来创建多少工作线程。

  • 创建流程

    1. 分配线程池结构内存。

    2. 初始化同步原语:互斥锁、条件变量。

    3. 申请线程数组空间。

    4. 启动指定数量的线程,线程执行thread_do_work函数。

    5. 返回线程池实例指针。

  • 错误处理
    如果分配内存或创建线程失败,要释放已分配资源,防止内存泄漏。


4. 向线程池添加任务 threadpool_add

int threadpool_add(threadpool_t *pool, void (*function)(void *), void *arg);
  • 流程

    1. 创建任务结构体,分配内存,并设置函数指针和参数。

    2. 加锁保护任务队列,防止数据冲突。

    3. 将新任务插入队尾,保持队列 FIFO(先进先出)顺序。

    4. 发送信号通知等待线程有新任务可执行。

    5. 解锁,返回0表示成功。

  • 加锁保护关键
    任务队列是共享资源,多个线程同时读取或写入会造成竞态条件和数据破坏。


5. 工作线程执行函数 thread_do_work

void *thread_do_work(void *arg);
  • 参数
    传入线程池指针,方便访问任务队列及同步对象。

  • 执行流程

    1. 线程进入无限循环,等待任务。

    2. 加锁,检查任务队列是否空。

    3. 队列空则阻塞等待条件变量信号(表示有任务到来)。

    4. 检查是否需要退出(shutdown为真)。

    5. 从队列头获取任务,调整队列指针。

    6. 解锁,执行任务函数。

    7. 释放任务结构体内存,继续下一轮循环。

  • 阻塞等待的意义
    避免CPU空忙,线程只有在收到信号时才唤醒执行,有新任务及时响应。


6. 销毁线程池函数 threadpool_destroy

void threadpool_destroy(threadpool_t *pool);
  • 作用
    关闭线程池,释放所有资源。

  • 流程

    1. 设置shutdown标志。

    2. 广播条件变量,唤醒所有等待线程,让它们退出循环。

    3. 主线程通过pthread_join等待所有工作线程安全退出。

    4. 清理剩余任务(回收任务队列所有任务内存)。

    5. 销毁锁和条件变量。

    6. 释放线程数组和线程池结构体内存。

  • 注意
    正确通知并等待线程退出,避免程序异常崩溃。


7. 任务执行示例函数 task_function

void task_function(void *arg);
  • 任务例子非常简单:打印线程ID和任务编号,然后模拟耗时1秒。

  • 传参时,外部分配一个整型的指针,传入任务。


8. 主函数 main

  • 创建4线程线程池。

  • 添加10个打印任务。

  • 中间用sleep(5)给线程池时间执行任务(实际项目可用同步手段)。

  • 销毁线程池,释放资源。


9. 代码整体结构回顾

|-- 头文件
|-- 任务结构体定义(函数指针+参数+链表指针)
|-- 线程池结构体定义(锁、条件变量、线程、队列指针、标志)
|-- 线程池创建函数(初始化锁、条件变量,创建线程)
|-- 添加任务函数(分配任务结构,加入任务队列,发信号)
|-- 工作线程函数(循环等待任务,执行任务,退出)
|-- 销毁线程池函数(设置退出标志,唤醒线程,等待线程结束,释放资源)
|-- 示例任务函数(打印数字,模拟耗时)
|-- main函数(创建线程池,添加任务,销毁线程池)

完整代码

下面是线程池的完整示例代码,注释中有详细说明。代码简单明了,适合初学者理解。

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

// 任务结构体,包含函数指针和参数
typedef struct task_t {
    void (*function)(void *);
    void *arg;
    struct task_t *next;
} task_t;

// 线程池结构体
typedef struct threadpool_t {
    pthread_mutex_t lock;          // 互斥锁保护任务队列
    pthread_cond_t notify;         // 条件变量,任务队列非空时唤醒线程
    pthread_t *threads;            // 工作线程数组
    task_t *task_queue_head;       // 任务队列头
    task_t *task_queue_tail;       // 任务队列尾
    int thread_count;              // 线程个数
    int shutdown;                  // 线程池关闭标志
} threadpool_t;

// 线程池初始化
threadpool_t *threadpool_create(int thread_count);

// 向线程池添加任务
int threadpool_add(threadpool_t *pool, void (*function)(void *), void *arg);

// 线程池销毁
void threadpool_destroy(threadpool_t *pool);

// 工作线程执行函数
void *thread_do_work(void *arg);

// 简单演示任务函数
void task_function(void *arg);

//////////////////// 实现 ////////////////////

threadpool_t *threadpool_create(int thread_count) {
    threadpool_t *pool = malloc(sizeof(threadpool_t));
    if (!pool) {
        perror("malloc threadpool");
        return NULL;
    }
    pool->thread_count = thread_count;
    pool->shutdown = 0;
    pool->task_queue_head = NULL;
    pool->task_queue_tail = NULL;
    pool->threads = malloc(sizeof(pthread_t) * thread_count);
    if (!pool->threads) {
        perror("malloc threads");
        free(pool);
        return NULL;
    }
    pthread_mutex_init(&pool->lock, NULL);
    pthread_cond_init(&pool->notify, NULL);

    // 创建线程
    for (int i = 0; i < thread_count; i++) {
        if (pthread_create(&pool->threads[i], NULL, thread_do_work, pool) != 0) {
            perror("pthread_create");
            threadpool_destroy(pool);
            return NULL;
        }
    }
    return pool;
}

int threadpool_add(threadpool_t *pool, void (*function)(void *), void *arg) {
    if (pool == NULL || function == NULL)
        return -1;

    task_t *new_task = malloc(sizeof(task_t));
    if (!new_task) {
        perror("malloc task");
        return -1;
    }
    new_task->function = function;
    new_task->arg = arg;
    new_task->next = NULL;

    pthread_mutex_lock(&pool->lock);

    if (pool->task_queue_tail == NULL) {
        // 第一个任务
        pool->task_queue_head = new_task;
        pool->task_queue_tail = new_task;
    } else {
        // 放置到队尾
        pool->task_queue_tail->next = new_task;
        pool->task_queue_tail = new_task;
    }

    // 任务队列非空,通知线程
    pthread_cond_signal(&pool->notify);
    pthread_mutex_unlock(&pool->lock);

    return 0;
}

void threadpool_destroy(threadpool_t *pool) {
    if (pool == NULL)
        return;

    pthread_mutex_lock(&pool->lock);
    pool->shutdown = 1;
    // 唤醒所有线程
    pthread_cond_broadcast(&pool->notify);
    pthread_mutex_unlock(&pool->lock);

    // 等待线程退出
    for (int i = 0; i < pool->thread_count; i++) {
        pthread_join(pool->threads[i], NULL);
    }

    // 清理剩余任务
    while (pool->task_queue_head) {
        task_t *tmp = pool->task_queue_head;
        pool->task_queue_head = pool->task_queue_head->next;
        free(tmp);
    }

    pthread_mutex_destroy(&pool->lock);
    pthread_cond_destroy(&pool->notify);

    free(pool->threads);
    free(pool);
}

void *thread_do_work(void *arg) {
    threadpool_t *pool = (threadpool_t *)arg;
    while (1) {
        pthread_mutex_lock(&pool->lock);

        // 等待任务
        while (pool->task_queue_head == NULL && !pool->shutdown) {
            pthread_cond_wait(&pool->notify, &pool->lock);
        }

        // 线程池关闭,退出线程
        if (pool->shutdown) {
            pthread_mutex_unlock(&pool->lock);
            pthread_exit(NULL);
        }

        // 取出任务
        task_t *task = pool->task_queue_head;
        if (task) {
            pool->task_queue_head = task->next;
            if (pool->task_queue_head == NULL)
                pool->task_queue_tail = NULL;
        }
        pthread_mutex_unlock(&pool->lock);

        // 执行任务
        if (task) {
            task->function(task->arg);
            free(task);
        }
    }
    return NULL;
}

// 简单任务示例:打印数字
void task_function(void *arg) {
    int num = *((int *)arg);
    printf("Thread %ld is processing task %d\n", pthread_self(), num);
    sleep(1);  // 模拟任务执行耗时
}

//////////////////// 测试示例 ////////////////////

int main() {
    threadpool_t *pool = threadpool_create(4);  // 创建4个线程的线程池
    if (!pool) {
        return -1;
    }

    // 投递10个任务给线程池
    for (int i = 0; i < 10; i++) {
        int *num = malloc(sizeof(int));
        *num = i + 1;
        threadpool_add(pool, task_function, num);
    }

    // 等待一段时间让所有任务完成
    sleep(5);  

    // 销毁线程池
    threadpool_destroy(pool);
    return 0;
}

代码运行说明

  • 程序创建一个含4个线程的线程池。

  • 向线程池添加了10个打印任务。

  • 每个任务打印线程ID和任务编号,然后睡眠1秒模拟耗时。

  • 主线程等待5秒后销毁线程池。

  • 这时线程池中所有任务执行完毕,所有线程也被正确关闭。


总结

希望这篇教程帮你顺利迈出多线程编程的重要一步!如有疑问,欢迎留言交流。

通过这些详细讲解,相信你会更清楚:

  • 线程池的核心是任务队列和工作线程。

  • 多线程编程必须注意同步,保证数据一致。

  • 通过条件变量让无任务的线程睡眠节省CPU资源。

  • 通过shutdown机制,线程池可安全关闭。

  • 代码用链表实现任务队列,便于扩展。

尝试自己写写看,调试程序运行,会对多线程和同步有深刻认识。

如需进一步深入,多学习pthread库的原理和用法,以及多线程设计模式。


祝你编程愉快!😊

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值