基于C语言的线程池设计与实现探讨

1. 线程池概述

线程池是一种预先创建多个线程,并将其统一管理的机制。在高并发环境下,通过复用线程来执行任务,线程池能够有效降低线程创建和销毁的频率,减少系统开销,同时提高系统的响应速度和吞吐量。此外,线程池还能对并发数进行限制,避免因线程数过多导致的资源竞争问题。

2. 代码结构概览

在所提供的代码中,主要包含两个结构体:

  • Task结构体:封装了任务函数指针及其参数,定义了线程池中每个任务的基本信息。
  • threadPool结构体:用于管理线程池的各项数据,包括最小和最大线程数、当前活跃线程数、忙碌线程数、任务队列(及其索引)、以及各种同步机制(互斥锁与条件变量)。

通过这种设计,可以较好地管理任务队列与线程状态,实现线程池的动态调节。

3. 线程池初始化与线程管理

3.1 初始化过程

create_threadPool函数中:

  • 为线程池、任务池和工作线程数组动态分配内存。
  • 初始化互斥锁和条件变量,保证后续多线程操作的同步安全。
  • 创建管理线程(manager)和初始工作线程(worker)。管理线程负责监控任务量和线程状态,而工作线程则负责执行任务。

这种设计确保线程池在启动时就具备基本的任务处理能力,同时预留了后续扩展的可能。

3.2 管理线程(Manager Thread)

管理线程周期性(代码中每隔3秒)检查任务队列与工作线程的状态:

  • 当任务数量大于现有线程数且未达到最大线程限制时,管理线程会增加新的工作线程(每次增加的数量由reamke_count决定)。
  • 当系统中空闲线程过多(即忙碌线程数量较低)且活跃线程数大于最小值时,会通过设置销毁线程数标识(exitnum)并发出信号,通知部分空闲线程退出。

这种动态扩缩容策略使线程池能够根据实际负载自动调节线程数量,既保证了高峰期的并发处理能力,又避免了资源浪费。

3.3 工作线程(Worker Thread)

工作线程在一个无限循环中执行以下操作:

  • 先获取互斥锁并检查任务队列是否为空或线程池是否处于关闭状态。
  • 如果任务队列为空,则通过条件变量isEmpty等待任务的到来;同时,在等待期间会检测是否存在多余线程需要销毁。
  • 当有任务可执行时,从任务队列中取出任务,并在执行前更新忙碌线程计数。
  • 执行任务函数,并在任务完成后释放任务参数内存,再更新忙碌线程计数。

这种设计既保证了任务的及时调度,又确保了线程退出时能正确更新线程池状态,从而实现了线程池的高效协作。

3.4 任务添加与同步控制

任务的添加通过add_task函数完成,该函数的主要流程包括:

  • 获取线程池锁,判断任务队列是否已满。如果满,则通过isFULL条件变量等待。
  • 将任务插入任务数组,并更新任务尾索引和任务计数。
  • 发出条件变量isEmpty信号,唤醒等待任务的工作线程。

这种机制利用了生产者—消费者模型,确保任务生产与消费之间的协调,从而避免竞争和死锁问题。

4. 线程池销毁机制

线程池的销毁由threadPool_destroy函数实现。主要步骤如下:

  • 将线程池状态标记为关闭(is_shutted置1),通知所有线程即将退出。
  • 通过pthread_join等待管理线程结束,并发信号唤醒所有等待的工作线程,促使它们检查关闭标志后退出。
  • 最后依次释放任务数组、工作线程数组以及销毁所有同步机制,确保资源得到充分清理。

这一机制保证了线程池在退出前能安全地终止所有线程,避免内存泄漏和悬挂线程问题。

5. 同步机制与线程安全

代码中使用了多种同步手段:

  • 互斥锁(Mutex):用于保护线程池中共享数据的读写,防止数据竞争。
  • 条件变量(Condition Variable)isEmpty用于控制任务等待,isFULL用于控制任务生产。条件变量使线程能够在满足特定条件时被唤醒,提高了资源利用率。
  • 忙碌计数与退出标识:通过busynumexitnum记录线程工作状态和销毁需求,实现了对线程数量的精细调控。

这些同步机制确保了多线程环境下的数据一致性和系统的稳定性。

6.头文件

#ifndef __threadpool_H
#define __threadpool_H

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

// 任务结构体:封装了任务函数及其参数
typedef struct Task {
    void (*func)(void* arg);
    void* arg;
} Task;

// 线程池结构体:保存线程池状态、任务队列、线程ID数组及同步机制
typedef struct threadPool {
    int min;        // 最小线程数
    int max;        // 最大线程数
    int livenum;    // 当前存活线程数
    int busynum;    // 正在执行任务的线程数
    int exitnum;    // 需要销毁的线程数

    Task* tasks;       // 任务队列(数组形式存储任务)
    int task_total;    // 任务队列的总容量
    int task_nums;     // 当前任务数量
    int task_head;     // 队首索引(取任务的位置)
    int task_end;      // 队尾索引(添加任务的位置)

    pthread_t manger_Id;     // 管理线程的ID
    pthread_t *worker_Ids;   // 工作线程的ID数组

    // 互斥锁和条件变量,用于线程间同步和保护共享数据
    pthread_mutex_t threadPool_mutex; 
    pthread_mutex_t busy_mutex;
    pthread_cond_t isFULL;   // 条件变量:任务队列是否已满
    pthread_cond_t isEmpty;  // 条件变量:任务队列是否为空

    int is_shutted; // 标记线程池是否关闭
} threadPool;

// 函数声明
threadPool* create_threadPool(int min, int max, int task_total);
void* manager(void* arg);
void* worker(void* arg);
void thread_exit(threadPool* pool);
int check_busynums(threadPool* pool);
int check_livenums(threadPool* pool);
void add_task(threadPool* pool, void (*func)(void* arg), void* arg);
void threadPool_destroy(threadPool* pool);

#endif

 7.线程池实现

// ------------------- 线程池实现 -------------------
#include "threadpool.h"

#define reamke_count 3  // 每次增减的工作线程数量

// 创建线程池,初始化所有数据并启动管理线程和工作线程
threadPool* create_threadPool(int min, int max, int task_total) {
    threadPool* pool = (threadPool*)malloc(sizeof(threadPool));
    if (pool == NULL) {
        printf("线程池初始化失败,请检查问题\n");
        return NULL;
    }

    pool->tasks = (Task*)malloc(sizeof(Task) * task_total);
    if (pool->tasks == NULL) {
        printf("任务池初始化失败,请检查问题\n");
        free(pool);
        return NULL;
    }

    pool->worker_Ids = (pthread_t*)calloc(max, sizeof(pthread_t));
    pool->manger_Id = 0;
    if (pool->worker_Ids == NULL) {
        printf("工人线程初始化失败\n");
        free(pool->tasks);
        free(pool);
        return NULL;
    }

    // 初始化互斥锁和条件变量
    if (pthread_mutex_init(&pool->threadPool_mutex, NULL) != 0 ||
        pthread_mutex_init(&pool->busy_mutex, NULL) != 0 ||
        pthread_cond_init(&pool->isEmpty, NULL) != 0 ||
        pthread_cond_init(&pool->isFULL, NULL) != 0) {
        printf("锁初始化失败\n");
        free(pool->tasks);
        free(pool->worker_Ids);
        free(pool);
        return NULL;
    }

    // 初始化线程池状态
    pool->busynum = 0;     
    pool->exitnum = 0;     
    pool->livenum = min;   
    pool->max = max;       
    pool->min = min;       
    pool->task_total = task_total;
    pool->task_nums = 0;
    pool->task_head = 0;
    pool->task_end = 0;
    pool->is_shutted = 0;

    // 创建管理线程,负责动态调整工作线程数
    int ret = pthread_create(&pool->manger_Id, NULL, manager, pool);
    if (ret != 0) {
        printf("管理者线程创建失败,错误码: %d\n", ret);
        free(pool->tasks);
        free(pool->worker_Ids);
        free(pool);
        return NULL;
    }

    // 根据最小线程数创建工作线程
    for (int i = 0; i < pool->min; ++i) {
        ret = pthread_create(&pool->worker_Ids[i], NULL, worker, pool);
        if (ret != 0) {
            printf("工作线程创建失败,错误码: %d\n", ret);
            free(pool->tasks);
            free(pool->worker_Ids);
            free(pool);
            return NULL;
        }
    }
    return pool;
}

// 工作线程函数:不断等待并执行任务
void* worker(void* arg) {
    threadPool* pool = (threadPool*)arg;
    while (1) {
        pthread_mutex_lock(&pool->threadPool_mutex);
        // 如果任务队列为空且线程池未关闭,则等待任务
        if (pool->task_nums == 0 && !pool->is_shutted) {
            pthread_cond_wait(&pool->isEmpty, &pool->threadPool_mutex);
            // 判断是否有多余线程需要退出
            if (pool->exitnum > 0) {
                pool->exitnum--;
                if (pool->livenum > pool->min) {
                    pool->livenum--;
                    thread_exit(pool);
                }
            }
        }
        // 如果线程池处于关闭状态,则退出
        if (pool->is_shutted) {
            thread_exit(pool);
        }
        // 从任务队列中取出一个任务
        Task task = pool->tasks[pool->task_head];
        pool->task_head = (pool->task_head + 1) % pool->task_total;
        pool->task_nums--;

        // 通知添加任务的线程:队列中有空位了
        pthread_cond_signal(&pool->isFULL);
        pthread_mutex_unlock(&pool->threadPool_mutex);

        printf("工人:%ld 正在工作\n", pthread_self());
        // 更新忙碌线程计数
        pthread_mutex_lock(&pool->busy_mutex);
        pool->busynum++;
        pthread_mutex_unlock(&pool->busy_mutex);

        // 执行任务
        task.func(task.arg);
        // 任务完成后释放任务参数内存
        free(task.arg);
        printf("工人:%ld 已经完成工作\n", pthread_self());

        // 更新忙碌线程计数
        pthread_mutex_lock(&pool->busy_mutex);
        pool->busynum--;
        pthread_mutex_unlock(&pool->busy_mutex);
    }
    return NULL;
}

// 管理线程函数:定期检查任务队列和线程状态,动态增减工作线程
void* manager(void* arg) {
    threadPool* pool = (threadPool*)arg;
    while (1) {
        sleep(3);
        if (!pool->is_shutted) {
            pthread_mutex_lock(&pool->threadPool_mutex);
            int livenum = pool->livenum;
            int tasknums = pool->task_nums;
            pthread_mutex_unlock(&pool->threadPool_mutex);

            // 若任务数大于当前线程数且未达到最大线程数,则添加新的工作线程
            if (tasknums > livenum && livenum < pool->max) {
                pthread_mutex_lock(&pool->threadPool_mutex);
                int count = 0;
                for (int i = 0; i < pool->max && livenum < pool->max && count < reamke_count; ++i) {
                    if (pool->worker_Ids[i] == 0) {
                        pthread_create(&pool->worker_Ids[i], NULL, worker, pool);
                        pool->livenum++;
                        count++;
                    }
                }
                pthread_mutex_unlock(&pool->threadPool_mutex);
            }

            // 若空闲线程较多,则减少部分工作线程
            pthread_mutex_lock(&pool->busy_mutex);
            int busynums = pool->busynum;
            pthread_mutex_unlock(&pool->busy_mutex);
            if (busynums * 2 < pool->livenum && livenum > pool->min) {
                pthread_mutex_lock(&pool->threadPool_mutex);
                pool->exitnum = reamke_count;
                pthread_mutex_unlock(&pool->threadPool_mutex);
                for (int i = 0; i < reamke_count; ++i) {
                    pthread_cond_signal(&pool->isEmpty);
                }
            }
        }
    }
    return NULL;
}

// 添加任务到任务队列,使用条件变量保证队列不溢出
void add_task(threadPool* pool, void (*func)(void* arg), void* arg) {
    pthread_mutex_lock(&pool->threadPool_mutex);
    // 如果任务队列已满,则等待
    while (!pool->is_shutted && pool->task_nums == pool->task_total) {
        pthread_cond_wait(&pool->isFULL, &pool->threadPool_mutex);
    }
    if (pool->is_shutted) {
        pthread_mutex_unlock(&pool->threadPool_mutex);
        return;
    }
    // 将任务添加到队尾
    pool->tasks[pool->task_end].func = func;
    pool->tasks[pool->task_end].arg = arg;
    pool->task_end = (pool->task_end + 1) % pool->task_total;
    pool->task_nums++;

    // 通知等待任务的工作线程
    pthread_cond_signal(&pool->isEmpty);
    pthread_mutex_unlock(&pool->threadPool_mutex);
}

// 检查当前忙碌的线程数
int check_busynums(threadPool* pool) {
    int val = 0;
    pthread_mutex_lock(&pool->busy_mutex);
    val = pool->busynum;
    pthread_mutex_unlock(&pool->busy_mutex);
    return val;
}

// 检查当前存活的线程数
int check_livenums(threadPool* pool) {
    int val = 0;
    pthread_mutex_lock(&pool->threadPool_mutex);
    val = pool->livenum;
    pthread_mutex_unlock(&pool->threadPool_mutex);
    return val;
}

// 工作线程退出函数:将自己的线程ID置0,并调用pthread_exit退出
void thread_exit(threadPool* pool) {
    int id = pthread_self();
    for (int i = 0; i < pool->max; ++i) {
        if (id == pool->worker_Ids[i]) {
            pool->worker_Ids[i] = 0;
            printf("我被迫辞职不干 我是工人:%ld\n", pthread_self());
            break;
        }
    }
    pthread_exit(NULL);
}

// 销毁线程池:关闭所有线程、释放所有资源
void threadPool_destroy(threadPool* pool) {
    if (pool == NULL) {
        return;
    }
    pool->is_shutted = 1;
    pthread_join(pool->manger_Id, NULL);
    // 唤醒所有等待中的工作线程,使其检查关闭标志后退出
    for (int i = 0; i < pool->livenum; ++i) {
        pthread_cond_signal(&pool->isEmpty);
    }
    // 释放任务队列和工作线程数组
    if (pool->tasks) free(pool->tasks);
    if (pool->worker_Ids) free(pool->worker_Ids);

    // 销毁互斥锁和条件变量
    pthread_mutex_destroy(&pool->threadPool_mutex);
    pthread_mutex_destroy(&pool->busy_mutex);
    pthread_cond_destroy(&pool->isEmpty);
    pthread_cond_destroy(&pool->isFULL);

    // 释放线程池结构体
    free(pool);
    pool = NULL;
    return;
}

 8.主函数

#include "threadpool.h"

// 示例任务函数:输出线程ID和传入的数字,模拟工作1秒
void taskFunc(void* arg) {
    int num = *(int*)arg;
    printf("thread %ld is working, number = %d\n", pthread_self(), num);
    sleep(1);
}

int main() {
    // 注意:main函数中使用的接口名称与上面定义的名称略有不一致,
    // 实际使用时应保持统一,这里假设你在调用时做了相应的适配。
    threadPool* pool = create_threadPool(5, 50, 100);
    for (int i = 0; i < 100; ++i) {
        int* num = (int*)malloc(sizeof(int));
        *num = i + 100;
        add_task(pool, taskFunc, num);
    }

    // 让线程池运行一段时间,等待所有任务完成
    sleep(30);

    threadPool_destroy(pool);
    return 0;
}

9、总结

本文实现了一个基于C语言的简易线程池,涵盖了任务队列、工作线程管理、任务添加、线程池销毁等核心模块。通过对比常见的线程池设计,读者可以更深入理解线程池的底层机制。实际开发中,可以根据具体需求对线程池进行优化和扩展,如调整任务队列的大小、动态调整线程数量等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值