[Linux程序设计] C语言实现线程池

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

一、线程池是什么?

线程池是一种管理和复用线程的机制,它以一组预先创建的线程来执行任务,而不是为每个任务都创建一个新线程。线程池的基本原理如下:

初始化线程池:在线程池被创建时,会初始化一定数量的线程,并将它们置于就绪状态,等待任务的到来。

接收任务:当有任务需要执行时,任务会被提交到线程池中。线程池管理器会从任务队列中获取任务。

线程分配任务:线程池管理器从可用的线程池中选择一个空闲的线程,将任务分配给该线程进行执行。

任务执行:被选中的线程会执行被分配的任务。执行完任务后,线程并不会被销毁,而是继续保持活动状态,等待下一个任务的到来。

任务完成和回收:当任务执行完毕后,线程池管理器会将线程标记为空闲状态,并将线程返回到线程池中,以供下一个任务使用。

通过使用线程池,可以获得以下几个优点

减少线程创建和销毁的开销:线程的创建和销毁是有成本的,通过线程池可以减少这些开销。预先创建一组线程,并重用它们,避免频繁地创建和销毁线程。

控制并发线程数量:线程池可以限制并发执行的线程数量,避免因为线程过多而导致系统资源耗尽。

提高任务调度效率:通过任务队列和线程池管理器,可以更好地控制任务执行的顺序和优先级,提高任务调度和执行的效率。

提供线程管理和监控功能:线程池还可以提供监控线程状态、统计任务执行情况、灵活调整线程池参数等管理功能。

需要注意的是,在使用线程池时,应根据实际需求合理配置线程池的大小和相关参数,以避免线程饥饿、线程过多或任务阻塞等问题。同时,在设计任务时,要考虑线程安全性和任务的可分解性,以便更好地发挥线程池的性能优势。

二、使用步骤

1.创建任务和线程池结构体

代码如下(示例):

//任务结构体
struct task_s            
{
    /* data */
    void (*function)(void *arg);    // 传入函数
    void *arg;                      // 函数参数
};

// 线程池结构体
struct ThreadPool_s
{

    Task*           taskQ;          // 任务队列
    int             queueCapacity;  // 队列容量
    int             queueSize;      // 当前任务数量
    int             queueFront;     // 队头
    int             queueRear;      // 队尾

    pthread_t       managerID;      // 管理者线程ID
    pthread_t*      threadIDs;      // 工作者线程ID

    int             minNum;         // 最小线程数量
    int             maxNum;         // 最大线程数量
    int             busyNum;        // 忙的线程的个数
    int             liveNum;        // 存活的线程个数
    int             exitNum;        // 要结束的线程个数
    
    pthread_mutex_t mutexPool;      // 锁整个的线程池
    pthread_mutex_t mutexBusy;      // 锁busyNum变量
    pthread_cond_t  notFull;        // 任务队列是不是满了
    pthread_cond_t  notEmpty;       // 任务队列是不是空了

    int             shutdown;       //是否销毁线程池,销毁为1,不销毁为0
    
};

typedef struct ThreadPool_s ThreadPool;    // 结构体类型起别名

2.线程池初始化

代码如下(示例):

/*
 参数一:线程池最小线程数量 
 参数二:线程池最大线程数量  
 参数三:一个线程能够执行任务数
 初始化成功返回线程池指针对象,失败返回NULL
*/
ThreadPool *threadPoolCreate(const int min, const int max, const int queueSize)
{
    ThreadPool* pool = (ThreadPool *)calloc(sizeof(ThreadPool), 1); //  创建线程池指针对象并开辟空间
    do
    {
        if (pool == NULL)                       //   如果分配空间失败,跳出do...while(0)
        {
            perror("fail to create ThreadPool");            
            break;
        }
        if((pool->threadIDs = (pthread_t *)calloc(sizeof(pthread_t), max)) == NULL)
        {
            perror("fail to create threadIDs");
            break;
        }
        pool->minNum  = min;
        pool->maxNum  = max;
        pool->busyNum = 0;
        pool->liveNum = min;
        pool->exitNum = 0;
        
        if (pthread_mutex_init(&pool->mutexPool, NULL) != 0 || pthread_mutex_init(&pool->mutexBusy, NULL) != 0 || pthread_cond_init(&pool->notEmpty, NULL) != 0 || pthread_cond_init(&pool->notFull, NULL) != 0)
        {
            perror("fail to mutex or cond");
            break;
        }

        pool->taskQ = (Task *)calloc(sizeof(Task), queueSize);
        pool->queueCapacity = queueSize;
        pool->queueSize  = 0;
        pool->queueFront = 0;
        pool->queueRear  = 0;
        pool->shutdown   = 0;

        pthread_create(&pool->managerID, NULL, manager, pool);       // manager为管理者函数,只有一个

        for (int i=0; i<min; i++)
        {
            pthread_create(&pool->threadIDs[i], NULL, worker, pool); // worker为工作者线程,具有多个
        }
        return pool;

    }while(0);

    if (pool && pool->threadIDs) free(pool->threadIDs);
    if (pool && pool->taskQ)     free(pool->taskQ);
    if (pool)                    free(pool);
    return NULL;
}

 3. 工作者和管理者

代码如下(示例):

// 一次减少或者增加的线程数为2
#define     NUMBER      2

static void *worker(void *arg)
{
    ThreadPool *pool = (ThreadPool *)arg;
    while(1)
    {
        pthread_mutex_lock(&pool->mutexPool);
        // 当前任务队列是否为空
        while (pool->queueSize == 0 && !pool->shutdown)
        {
            // 阻塞工作进程,等待信号
            pthread_cond_wait(&pool->notEmpty, &pool->mutexPool);
            if (pool->exitNum > 0)
            {
                pool->exitNum--;
                if (pool->liveNum > pool->minNum)	// 线程池中工程线程大于最小线程数才减少线程数
                {
                    pool->liveNum--;
                    pthread_mutex_unlock(&pool->mutexPool);
                    threadExit(pool);
                }
            }
        }
        // 判断线程池是否被关闭了
        if (pool->shutdown)
        {
            pthread_mutex_unlock(&pool->mutexPool);
            threadExit(pool);
        }
        // 取出任务
        Task task;
        
        task.function = pool->taskQ[pool->queueFront].function;
        task.arg  = pool->taskQ[pool->queueFront].arg;

        pool->queueFront = (pool->queueFront + 1) % pool->queueCapacity;
        pool->queueSize--;

        pthread_cond_signal(&pool->notFull);        // 唤醒管理者
        pthread_mutex_unlock(&pool->mutexPool);

        printf("thread %ld start working\r\n", pthread_self());
        pthread_mutex_lock(&pool->mutexBusy);
        pool->busyNum++;
        pthread_mutex_unlock(&pool->mutexBusy);
        task.function(task.arg);
        free(task.arg);			// 调用任务函数
        task.arg = NULL;
        //(*task.function)(task.arg);   // 解引用
        printf("thread %ld end working\r\n", pthread_self());
        pthread_mutex_lock(&pool->mutexBusy);
        pool->busyNum--;
        pthread_mutex_unlock(&pool->mutexBusy);
    }
    return NULL;

}

static void *manager(void *arg)
{
    ThreadPool* pool = (ThreadPool *)arg;
    while(!pool->shutdown)
    {
        sleep(3);
        pthread_mutex_lock(&pool->mutexPool);
        int queueSize = pool->queueSize;
        int liveNum = pool->liveNum;
        pthread_mutex_unlock(&pool->mutexPool);

        pthread_mutex_lock(&pool->mutexBusy);
        int busyNum = pool->busyNum;
        pthread_mutex_unlock(&pool->mutexBusy);

        // 线程少了
        if (queueSize > liveNum && liveNum < pool->maxNum)
        {
            int counter = 0;
            pthread_mutex_lock(&pool->mutexPool);
            for (int i=0; i<pool->maxNum && counter<NUMBER && pool->liveNum<pool->maxNum; ++i)
            {
                if (pool->threadIDs[i] == 0)
                {
                    pthread_create(&pool->threadIDs[i], NULL, worker, pool);	//	唤醒工作者,诱导其自动关闭
                    counter++;
                    pool->liveNum++;
                }
            }
            pthread_mutex_unlock(&pool->mutexPool);
        }

        // 线程多了
        if (busyNum*2 < liveNum && liveNum > pool->minNum)
        {
            pthread_mutex_lock(&pool->mutexPool);
            pool->exitNum = NUMBER;
            pthread_mutex_unlock(&pool->mutexPool);

            for (int i=0; i < NUMBER; ++i)
            {
                pthread_cond_signal(&pool->notEmpty);	// 唤醒管理者
            }
        }

    }
    return NULL;
}

4.线程退出函数

void threadExit(ThreadPool* pool)
{
    pthread_t tid = pthread_self();
    for (int i=0; i<pool->maxNum; i++)
    {
        if (pool->threadIDs[i] == tid)
        {
            pool->threadIDs[i] = 0;
            printf("threadExit() called, %ld exiting...\n", tid);
            break;
        }
    }
    pthread_exit(NULL);
}

5.其余操作函数

// 获取线程池中工作的线程的个数
int threadPoolBushNum(ThreadPool *pool)
{
    int busyNum = 0;
    pthread_mutex_lock(&pool->mutexPool);
    busyNum = pool->busyNum;
    pthread_mutex_unlock(&pool->mutexPool);
    return busyNum;
}

// 获取线程池中活着的线程的个数
int threadPoolAliveNum(ThreadPool *pool)
{
    int liveNum = 0;
    pthread_mutex_lock(&pool->mutexPool);
    liveNum = pool->liveNum;
    pthread_mutex_unlock(&pool->mutexPool);
    return liveNum;
}

5. 主函数测试

#include "./main.h"
#include "./threadpool.h"

void taskFunc(void *arg);

int main(int argc, char *argv[])
{
    ThreadPool *Pool = threadPoolCreate(3, 10, 100);;
    for (int i = 0; i < 100; i++)
    {
        int *num = (int *)calloc(sizeof(int), 1);
        *num = i + 100;
        threadPoolAdd(Pool, taskFunc, num);
    }
    sleep(30);            // 让主线程一直执行
    threadPoolDestory(Pool);
    return 0;
}

void taskFunc(void *arg)
{
    int num = *(int *)arg;
    printf("thread %ld is working, tid = %d\n", pthread_self(), num);
    sleep(1);
}

总结

经过测试,线程池可以使用。

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值