【C语言和C++】实现线程池

目录

一、线程池总体结构

二、线程数组

三、添加任务队列

四、管理者线程

五 、销毁

六 、其他子函数

七 、测试代码

 附录(C和C++代码)


最后有全部代码,急需可以跳过

线程池是一种概念,用什么语言实现只是具体概念的一种表现,本质上还是一样的,只是实现细节不一样而已

线程池(thread pool)技术是指能够保证所创建的任一线程都处于繁忙状态,而不需要频繁地为了某一任务而创建和销毁线程,因为系统在创建和销毁线程时所耗费的cpu资源很大。如果任务很多,频率很高,为了单一一个任务而起线程而后销线程,那么这种情况效率相当低下的。线程池技术就是用于解决这样一种应用场景而应运而生的。

线程池技术的工作原理:在起先就创建一定数量的线程以队列形式存在,并为其分配一个工作队列,当工作队列为空时,表示没有任务,此时所有线程挂起等待新的工作到来。当新的工作到来时,线程队列头开始执行这个任务,然后依次是第二、第三个线程执行新到来的任务,当其中某个线程处理完任务后,那么该线程立马开始接受任务分派,从而让所有线程都处于忙碌的状态,提高并行处理效率。

在Unix网络编程中,线程与进程用于处理各项分支子功能,我们通常的操作是:接收消息 ==> 消息分类 ==> 线程创建 ==> 传递消息到子线程 ==> 线程分离 ==> 在子线程中执行任务 ==> 任务结束退出; 
对大多数小型局域网的通信来说,上述方法足够满足需求;但当我们的通信范围扩大到广域网或大型局域网通信中时,我们将面临大量消息频繁请求服务器;在这种情况下,创建与销毁线程都已经成为一种奢侈的开销,特别对于嵌入式服务器来说更应保证内存资源的合理利用; 
因此,线程池技术应运而生;线程池允许一个线程可以多次复用,且每次复用的线程内部的消息处理可以不相同,将创建与销毁的开销省去而不必来一个请求开一个线程;

结构讲解: 
线程池是一个抽象的概念,其内部由任务队列,一堆线程,管理者线程组成;

我们将以上图为例,实现一个最基础的线程池,接下来将分部分依次讲解;讲解顺序为:1.线程池总体结构 2.线程数组 3.任务队列 4.管理者线程 5.使用线程池接口的例子 


一、线程池总体结构


这里讲解线程池在逻辑上的结构体;看下方代码,该结构体threadpool_t中包含线程池状态信息,任务队列信息以及多线程操作中的互斥锁;在任务结构体中包含了一个可以放置多种不同任务函数的函数指针,一个传入该任务函数的void*类型的参数; 
注意:在使用时需要将你的消息分类处理函数装入任务的(*function);然后放置到任务队列并通知空闲线程;

线程池状态信息:描述当前线程池的基本信息,如是否开启、最小线程数、最大线程数、存活线程数、忙线程数、待销毁线程数等… … 
任务队列信息:描述当前任务队列基本信息,如最大任务数、队列不为满条件变量、队列不为空条件变量等… … 
多线程互斥锁:保证在同一时间点上只有一个线程在任务队列中取任务并修改任务队列信息、修改线程池信息; 
函数指针:在打包消息阶段,将分类后的消息处理函数放在(*function); 
void*类型参数:用于传递消息处理函数需要的信息;
 

typedef struct Task
{
    void (*function)(void * arg);
    void * arg;
}Task;

// 线程池结构体
struct ThreadPool
{
    // 任务队列
    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
};
/*
*threadPoolCreate:创建线程池并初始化
int min:直接可以用的工作线程数量
int max:最大工作的线程数量
int queueSize:创建任务队列的数量
返回值:创建并初始化的一个线程池
*/
ThreadPool* threadPoolCreate(int min, int max, int queueSize)
{
    ThreadPool * pool = (ThreadPool *)malloc(sizeof(ThreadPool));
    do
    {
        if(pool == NULL)
        {
            printf("malloc ThreadPool failed\n");
            break;
        }
        pool->threadIDs = (pthread_t*)malloc(sizeof(pthread_t)*max);
        if(pool->threadIDs == NULL)
        {
            printf("malloc threadIDs failed\n");
            break;
        }
        //memset(pool->threadIDs,0,sizeof(pthread_t) * max);
        for(int i = 0; i < max; i++)
        {
            pool->threadIDs[i] = 0;
            printf("%ld\n",pool->threadIDs[i]);
        }
        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)
        {
            printf("mutex or cond init failed\n");
            break;
        }

        //任务队列
        pool->taskQ = (Task*)malloc(sizeof(Task) * queueSize);
        pool->queueCapacity = queueSize;
        pool->queueFront = 0;
        pool->queueRear = 0;
        pool->shutdown = 0;

        //创建线程
        error = pthread_create(&pool->managerID,NULL,manager,pool);
        if(error != 0)
        {
            perror("create pthread_create managerID failed");
            break;
        }
        for(int i = 0;i < min; i++)
        {
            pthread_create(&pool->threadIDs[i],NULL,worker,pool);
            if(error != 0)
            {
                perror("create pthread_create threadIDs failed");
                break;
            }
        }
        return pool;
    }while(0);

    //初始化失败,释放资源
    if(pool && pool->threadIDs) free(pool->threadIDs);
    if(pool && pool->taskQ) free(pool->taskQ);
    if(pool) free(pool);

    return NULL;
}

二、线程数组

线程数组实际上是在线程池初始化时开辟的一段存放一堆线程tid的空间,在逻辑上形成一个池,里面放置着提前创建的线程;这段空间中包含了正在工作的线程等待工作的线程(空闲线程),等待被销毁的线程申明但没有初始化的线程空间; 
这里写图片描述

// 工作的线程(消费者线程)任务函数
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...\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;

        printf("thread %ld end working...\n",pthread_self()); 
        pthread_mutex_lock(&pool->mutexBusy);
        pool->busyNum--;
        pthread_mutex_unlock(&pool->mutexBusy);   
    }
    return NULL;
}

三、添加任务队列


任务队列的存在形式与线程数组相似;在线程池初始化时根据传入的最大任务数开辟空间;当服务器前方后请求到来后,分类并打包消息成为任务,将任务放入任务队列并通知空闲线程来取;不同之处在于任务队列有明显的先后顺序,先进先出;而线程数组中的线程则是一个竞争关系去拿到互斥锁争取任务; 

/*
*threadPoolAdd:给线程池添加任务
ThreadPool* pool:线程池
void(*func)(void*):要加到任务中的函数指针
void* arg:要加到任务中的函数指针的参数
返回值:无
*/
void threadPoolAdd(ThreadPool* pool, void(*func)(void*), void* arg)
{
    pthread_mutex_lock(&pool->mutexPool);
    while(pool->queueSize == pool->queueCapacity && !pool->shutdown)
    {
        //因为任务队列满了或者线程池需要销毁,阻塞生产者线程
        pthread_cond_wait(&pool->notFull, &pool->mutexPool);
    }
    if(pool->shutdown)
    {
        pthread_mutex_unlock(&pool->mutexPool);
        return ;
    }
    //添加任务
    pool->taskQ[pool->queueRear].function = func;
    pool->taskQ[pool->queueRear].arg = arg;
    pool->queueRear = (pool->queueRear + 1) % pool->queueCapacity;
    pool->queueSize++;

    pthread_cond_signal(&pool->notEmpty);
    pthread_mutex_unlock(&pool->mutexPool);
}

四、管理者线程


作为线程池的管理者,该线程的主要功能包括:检查线程池内线程的存活状态,工作状态;负责根据服务器当前的请求状态去动态的增加或删除线程,保证线程池中的线程数量维持在一个合理高效的平衡上; 
说到底,它就是一个单独的线程,定时的去检查,根据我们的一个维持平衡算法去增删线程
 

// 管理者线程任务函数
void* manager(void* arg)
{
    ThreadPool * pool = (ThreadPool *)arg;
    while(!pool->shutdown)
    {
        //3s检查一次
        sleep(3);
        //取出线程池任务的数量和当前线程的数量
        pthread_mutex_lock(&pool->mutexPool);
        int queueSize = pool->queueSize;
        int liveNum = pool->liveNum;
        printf("liveNum = %d\n",liveNum);
        pthread_mutex_unlock(&pool->mutexPool);
        //取出忙的线程的数量
        pthread_mutex_lock(&pool->mutexBusy);
        int busyNum = pool->busyNum;
        printf("busyNum = %d\n",busyNum);
        pthread_mutex_unlock(&pool->mutexBusy); 
        //添加线程
        //任务的个数 > 成活的线程个数 && 存活的线程数 < 最大线程数
        if(queueSize > liveNum && liveNum < pool->maxNum)
        {
            pthread_mutex_lock(&pool->mutexPool);
            for(int i = 0; i < pool->maxNum && pool->liveNum < pool->maxNum; i++)
            {
                if(pool->threadIDs[i] == 0)
                {
                    pthread_create(&pool->threadIDs[i],NULL,worker,pool);
                    pool->liveNum++;
                }
            }
            pthread_mutex_unlock(&pool->mutexPool);
        }
        //销毁线程
        //条件是:忙的线程*2 < 存活的线程数 && 存活的线程 > 最小线程数
        //我觉得,释放多余内存
        if(busyNum * 2 < liveNum && liveNum > pool->minNum)
        {
            pthread_mutex_lock(&pool->mutexPool);
            liveNum = pool->liveNum;
            busyNum = pool->busyNum;
            int NUMBER = liveNum-busyNum;
            pool->exitNum = NUMBER;
            pthread_mutex_unlock(&pool->mutexPool);
            //发信号让线程自杀,具体实现就是每个线程的有一个销毁判断
            //每次销毁一个,pool->exitNum--,直到为0
            for(int i = 0 ;i < NUMBER; i++)
            {
                pthread_cond_signal(&pool->notEmpty);
            }
        }
    }
    return NULL;
}

五 、销毁

/*
*threadPoolDestroy:销毁线程池
ThreadPool* pool:需要销毁的线程池
返回值:失败返回 -1,成功返回 0;
*/
int threadPoolDestroy(ThreadPool* pool)
{
    if(pool == NULL)
    {
        return -1;
    }
    //关闭线程池
    pool->shutdown = -1;
    //阻塞回收管理者线程
    pthread_join(pool->managerID,NULL);
    //唤醒阻塞的消费者线程 = 工作线程
    for(int i = 0; i < pool->liveNum; i++)
    {
        pthread_cond_signal(&pool->notEmpty);
    }
    //释放堆内存
    if(pool->taskQ)
    {
        free(pool->taskQ);
    }
    if(pool->threadIDs)
    {
        free(pool->threadIDs);
    }
    //销毁互斥量,条件变量
    pthread_mutex_destroy(&pool->mutexPool);
    pthread_mutex_destroy(&pool->mutexBusy);
    pthread_cond_destroy(&pool->notEmpty);
    pthread_cond_destroy(&pool->notFull);
    
    free(pool);
    pool = NULL;

    return 0;
}

六 、其他子函数

/*
*threadExit:单个线程退出
ThreadPool* pool:需要退出的线程
*/
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);
    return;
}
/*
*threadPoolBusyNum:获取线程池中工作的线程的个数
ThreadPool* pool:线程池
返回值:当前工作线程个数
*/
int threadPoolBusyNum(ThreadPool* pool)
{
    pthread_mutex_lock(&pool->mutexBusy);
    int busyNum = pool->busyNum;
    pthread_mutex_unlock(&pool->mutexBusy);
    return busyNum;
}
/*
*threadPoolBusyNum:获取线程池中活着的线程的个数
ThreadPool* pool:线程池
返回值:当前活着的线程的个数
*/
int threadPoolAliveNum(ThreadPool* pool)
{
    pthread_mutex_lock(&pool->mutexPool);
    int aliveNum = pool->liveNum;
    pthread_mutex_unlock(&pool->mutexPool);
    return aliveNum;
}

七 、测试代码

//测试案例
void taskFunc(void * arg)
{
    int num = *(int *)arg;
    printf("taskFunc thread %ld is working, number = %d\n",pthread_self(),num);
    sleep(3);
}

int main()
{
    //创建线程池
    ThreadPool * pool = threadPoolCreate(5,15,100);
    for(int i = 0; i < 100; i++)
    {
        int * num = (int *)malloc(sizeof(int));
        *num = i+100;
        threadPoolAdd(pool,taskFunc,num);
    }

    //主线程等待子线程完成
    while(1);
    sleep(30);
    threadPoolDestroy(pool);
    return 0;
}

 附录(代码)

#include<stdio.h>
#include<pthread.h>
#include <stdlib.h>
#include <unistd.h>
typedef struct Task
{
    void (*function)(void * arg);
    void * arg;
}Task;

// 线程池结构体
struct ThreadPool
{
    // 任务队列
    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 ThreadPool;
// 创建线程池并初始化
ThreadPool* threadPoolCreate(int min, int max, int queueSize);

// 销毁线程池
int threadPoolDestroy(ThreadPool* pool);

// 给线程池添加任务
void threadPoolAdd(ThreadPool* pool, void(*func)(void*), void* arg);

// 获取线程池中工作的线程的个数
int threadPoolBusyNum(ThreadPool* pool);

// 获取线程池中活着的线程的个数
int threadPoolAliveNum(ThreadPool* pool);

// 工作的线程(消费者线程)任务函数
void* worker(void* arg);
// 管理者线程任务函数
void* manager(void* arg);
// 单个线程退出
void threadExit(ThreadPool* pool);

int error;  //错误码

/*
*threadPoolCreate:创建线程池并初始化
int min:直接可以用的工作线程数量
int max:最大工作的线程数量
int queueSize:创建任务队列的数量
返回值:创建并初始化的一个线程池
*/
ThreadPool* threadPoolCreate(int min, int max, int queueSize)
{
    ThreadPool * pool = (ThreadPool *)malloc(sizeof(ThreadPool));
    do
    {
        if(pool == NULL)
        {
            printf("malloc ThreadPool failed\n");
            break;
        }
        pool->threadIDs = (pthread_t*)malloc(sizeof(pthread_t)*max);
        if(pool->threadIDs == NULL)
        {
            printf("malloc threadIDs failed\n");
            break;
        }
        //memset(pool->threadIDs,0,sizeof(pthread_t) * max);
        for(int i = 0; i < max; i++)
        {
            pool->threadIDs[i] = 0;
            printf("%ld\n",pool->threadIDs[i]);
        }
        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)
        {
            printf("mutex or cond init failed\n");
            break;
        }

        //任务队列
        pool->taskQ = (Task*)malloc(sizeof(Task) * queueSize);
        pool->queueCapacity = queueSize;
        pool->queueFront = 0;
        pool->queueRear = 0;
        pool->shutdown = 0;

        //创建线程
        error = pthread_create(&pool->managerID,NULL,manager,pool);
        if(error != 0)
        {
            perror("create pthread_create managerID failed");
            break;
        }
        for(int i = 0;i < min; i++)
        {
            pthread_create(&pool->threadIDs[i],NULL,worker,pool);
            if(error != 0)
            {
                perror("create pthread_create threadIDs failed");
                break;
            }
        }
        return pool;
    }while(0);

    //初始化失败,释放资源
    if(pool && pool->threadIDs) free(pool->threadIDs);
    if(pool && pool->taskQ) free(pool->taskQ);
    if(pool) free(pool);

    return NULL;
}

/*
*threadPoolDestroy:销毁线程池
ThreadPool* pool:需要销毁的线程池
返回值:失败返回 -1,成功返回 0;
*/
int threadPoolDestroy(ThreadPool* pool)
{
    if(pool == NULL)
    {
        return -1;
    }
    //关闭线程池
    pool->shutdown = -1;
    //阻塞回收管理者线程
    pthread_join(pool->managerID,NULL);
    //唤醒阻塞的消费者线程 = 工作线程
    for(int i = 0; i < pool->liveNum; i++)
    {
        pthread_cond_signal(&pool->notEmpty);
    }
    //释放堆内存
    if(pool->taskQ)
    {
        free(pool->taskQ);
    }
    if(pool->threadIDs)
    {
        free(pool->threadIDs);
    }
    //销毁互斥量,条件变量
    pthread_mutex_destroy(&pool->mutexPool);
    pthread_mutex_destroy(&pool->mutexBusy);
    pthread_cond_destroy(&pool->notEmpty);
    pthread_cond_destroy(&pool->notFull);
    
    free(pool);
    pool = NULL;

    return 0;
}

/*
*threadPoolAdd:给线程池添加任务
ThreadPool* pool:线程池
void(*func)(void*):要加到任务中的函数指针
void* arg:要加到任务中的函数指针的参数
返回值:无
*/
void threadPoolAdd(ThreadPool* pool, void(*func)(void*), void* arg)
{
    pthread_mutex_lock(&pool->mutexPool);
    while(pool->queueSize == pool->queueCapacity && !pool->shutdown)
    {
        //因为任务队列满了或者线程池需要销毁,阻塞生产者线程
        pthread_cond_wait(&pool->notFull, &pool->mutexPool);
    }
    if(pool->shutdown)
    {
        pthread_mutex_unlock(&pool->mutexPool);
        return ;
    }
    //添加任务
    pool->taskQ[pool->queueRear].function = func;
    pool->taskQ[pool->queueRear].arg = arg;
    pool->queueRear = (pool->queueRear + 1) % pool->queueCapacity;
    pool->queueSize++;

    pthread_cond_signal(&pool->notEmpty);
    pthread_mutex_unlock(&pool->mutexPool);
}

/*
*threadPoolBusyNum:获取线程池中工作的线程的个数
ThreadPool* pool:线程池
返回值:当前工作线程个数
*/
int threadPoolBusyNum(ThreadPool* pool)
{
    pthread_mutex_lock(&pool->mutexBusy);
    int busyNum = pool->busyNum;
    pthread_mutex_unlock(&pool->mutexBusy);
    return busyNum;
}
/*
*threadPoolBusyNum:获取线程池中活着的线程的个数
ThreadPool* pool:线程池
返回值:当前活着的线程的个数
*/
int threadPoolAliveNum(ThreadPool* pool)
{
    pthread_mutex_lock(&pool->mutexPool);
    int aliveNum = pool->liveNum;
    pthread_mutex_unlock(&pool->mutexPool);
    return aliveNum;
}

// 工作的线程(消费者线程)任务函数
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...\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;

        printf("thread %ld end working...\n",pthread_self()); 
        pthread_mutex_lock(&pool->mutexBusy);
        pool->busyNum--;
        pthread_mutex_unlock(&pool->mutexBusy);   
    }
    return NULL;
}
// 管理者线程任务函数
void* manager(void* arg)
{
    ThreadPool * pool = (ThreadPool *)arg;
    while(!pool->shutdown)
    {
        //3s检查一次
        sleep(3);
        //取出线程池任务的数量和当前线程的数量
        pthread_mutex_lock(&pool->mutexPool);
        int queueSize = pool->queueSize;
        int liveNum = pool->liveNum;
        printf("liveNum = %d\n",liveNum);
        pthread_mutex_unlock(&pool->mutexPool);
        //取出忙的线程的数量
        pthread_mutex_lock(&pool->mutexBusy);
        int busyNum = pool->busyNum;
        printf("busyNum = %d\n",busyNum);
        pthread_mutex_unlock(&pool->mutexBusy); 
        //添加线程
        //任务的个数 > 成活的线程个数 && 存活的线程数 < 最大线程数
        if(queueSize > liveNum && liveNum < pool->maxNum)
        {
            pthread_mutex_lock(&pool->mutexPool);
            for(int i = 0; i < pool->maxNum && pool->liveNum < pool->maxNum; i++)
            {
                if(pool->threadIDs[i] == 0)
                {
                    pthread_create(&pool->threadIDs[i],NULL,worker,pool);
                    pool->liveNum++;
                }
            }
            pthread_mutex_unlock(&pool->mutexPool);
        }
        //销毁线程
        //条件是:忙的线程*2 < 存活的线程数 && 存活的线程 > 最小线程数
        //我觉得,释放多余内存
        if(busyNum * 2 < liveNum && liveNum > pool->minNum)
        {
            pthread_mutex_lock(&pool->mutexPool);
            liveNum = pool->liveNum;
            busyNum = pool->busyNum;
            int NUMBER = liveNum-busyNum;
            pool->exitNum = NUMBER;
            pthread_mutex_unlock(&pool->mutexPool);
            //发信号让线程自杀,具体实现就是每个线程的有一个销毁判断
            //每次销毁一个,pool->exitNum--,直到为0
            for(int i = 0 ;i < NUMBER; i++)
            {
                pthread_cond_signal(&pool->notEmpty);
            }
        }
    }
    return NULL;
}
/*
*threadExit:单个线程退出
ThreadPool* pool:需要退出的线程
*/
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);
    return;
}


//测试案例
void taskFunc(void * arg)
{
    int num = *(int *)arg;
    printf("taskFunc thread %ld is working, number = %d\n",pthread_self(),num);
    sleep(3);
}

int main()
{
    //创建线程池
    ThreadPool * pool = threadPoolCreate(5,15,100);
    for(int i = 0; i < 100; i++)
    {
        int * num = (int *)malloc(sizeof(int));
        *num = i+100;
        threadPoolAdd(pool,taskFunc,num);
    }

    //主线程等待子线程完成
    while(1);
    sleep(30);
    threadPoolDestroy(pool);
    return 0;
}

还不会的小伙伴,认真打一次代码就会了,别直接cv,动手很重要

其中存在调试printf,不需要的可以注释掉 

以下为C++版本,因为C++存在面向对象思想,所有实现会更方便,但是概念还是一样的

#ifndef _THREADPOOL_H
#define _THREADPOOL_H
#include <vector>
#include <queue>
#include <thread>
#include <iostream>
#include <stdexcept>
#include <condition_variable>
#include <memory> //unique_ptr
#include<assert.h>

const int MAX_THREADS = 1000; //最大线程数目

template <typename T>
class threadPool
{
public:
    threadPool(int number = 1);//默认开一个线程
    ~threadPool();
    std::queue<T *> tasks_queue;		   //任务队列

    bool append(T *request);//往请求队列<task_queue>中添加任务<T *>

private:
    //工作线程需要运行的函数,不断的从任务队列中取出并执行
    static void *worker(void *arg);
    void run();

private:
    std::vector<std::thread> work_threads; //工作线程

    std::mutex queue_mutex;
    std::condition_variable condition;  //必须与unique_lock配合使用
    bool stop;
};//end class

//构造函数,创建线程
template <typename T>
threadPool<T>::threadPool(int number) : stop(false)
{
    if (number <= 0 || number > MAX_THREADS)
        throw std::exception();
    for (int i = 0; i < number; i++)
    {
        std::cout << "created Thread num is : " << i <<std::endl;
        work_threads.emplace_back(worker, this);//添加线程
        //直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程。
    }
}
template <typename T>
inline threadPool<T>::~threadPool()
{

    std::unique_lock<std::mutex> lock(queue_mutex);
    stop = true;

    condition.notify_all();
    for (auto &ww : work_threads)
        ww.join();//可以在析构函数中join
}
//添加任务
template <typename T>
bool threadPool<T>::append(T *request)
{
    /*操作工作队列时一定要加锁,因为他被所有线程共享*/
    queue_mutex.lock();//同一个类的锁
    tasks_queue.push(request);
    queue_mutex.unlock();
    condition.notify_one(); //线程池添加进去了任务,自然要通知等待的线程
    return true;
}
//单个线程
template <typename T>
void *threadPool<T>::worker(void *arg)
{
    threadPool *pool = (threadPool *)arg;
    pool->run();//线程运行
    return pool;
}
template <typename T>
void threadPool<T>::run()
{
    while (!stop)
    {
        std::unique_lock<std::mutex> lk(this->queue_mutex);
        /* unique_lock() 出作用域会自动解锁 */
        this->condition.wait(lk, [this] { return !this->tasks_queue.empty(); });
        //如果任务为空,则wait,就停下来等待唤醒
        //需要有任务,才启动该线程,不然就休眠
        if (this->tasks_queue.empty())//任务为空,双重保障
        {
            assert(0&&"断了");//实际上不会运行到这一步,因为任务为空,wait就休眠了。
            continue;
        }
        else
        {
            T *request = tasks_queue.front();
            tasks_queue.pop();
            if (request)//来任务了,开始执行
                request->process();
        }
    }
}
#endif

测试代码:

#include "mythread.h"
#include<string>
#include<math.h>
using namespace std;
class Task
{
    public:
    void process()
    {
        //cout << "run........." << endl;
        //测试任务数量
        long i=1000000;
        while(i!=0)
        {
            int j = sqrt(i);
            i--;
        }
    }
};
int main(void)
{
    threadPool<Task> pool(6);//6个线程,vector
    std::string str;
    while (1)
    {
            Task *tt = new Task();
            //使用智能指针
            pool.append(tt);//不停的添加任务,任务是队列queue,因为只有固定的线程数
            cout<<"添加的任务数量: "<<pool.tasks_queue.size()<<endl;;
            delete tt;
    }
}

感谢:https://blog.csdn.net/qq_36359022/article/details/78796784

  • 8
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
### 回答1: 实现线程池的方法有很多,但是最常见的方法是使用队列来维护任务。每个线程都在队列中等待任务,当有新任务到达时,就从队列中取出一个任务并执行。这样,每个线程都可以在并行执行任务,而不需要创建新的线程。 在C语言实现线程池的代码如下: ``` #include <pthread.h> #include <stdio.h> #include <stdlib.h> #define NUM_THREADS 5 void *print_hello(void *threadid) { long tid; tid = (long)threadid; printf("Hello World! It's me, thread #%ld!\n", tid); pthread_exit(NULL); } int main(int argc, char *argv[]) { pthread_t threads[NUM_THREADS]; int rc; long t; for (t = 0; t < NUM_THREADS; t++) { printf("In main: creating thread %ld\n", t); rc = pthread_create(&threads[t], NULL, print_hello, (void *)t); if (rc) { printf("ERROR; return code from pthread_create() is %d\n", rc); exit(-1); } } pthread_exit(NULL); } ``` ### 回答2: 要实现线程池,首先需要先了解线程池的基本概念和原理。 线程池是一种用来管理和复用线程的技术,它能够维护一个线程队列,按需创建和销毁线程,并将任务分配给这些线程来执行。使用线程池可以提高程序的性能,减少线程创建和销毁的开销。 在C语言中,可以使用多线程的库来实现线程池,比如pthread库。下面是一个简单的用C语言实现线程池的步骤: 1. 定义线程池结构体:创建一个结构体来保存线程池的相关信息,如线程池的大小、任务队列、互斥锁、条件变量等。 2. 初始化线程池:在初始化函数中,需要对线程池中的各个成员进行初始化,如创建指定数量的线程、初始化互斥锁和条件变量等。 3. 定义任务函数:线程池的任务函数用于处理任务队列中的任务,根据具体需求来定义任务的执行逻辑。 4. 添加任务到线程池:当有新的任务时,将任务添加到任务队列中,并通过条件变量来通知线程池中的线程有新任务可执行。 5. 线程池中的线程获取任务并执行:在线程中循环检查任务队列,当有任务时,线程从任务队列中获取任务并执行。 6. 销毁线程池:在停止使用线程池时,要销毁线程池中的资源,包括线程的回收、互斥锁和条件变量的销毁等。 通过以上步骤,就可以在C语言实现一个简单的线程池。具体实现中还需要考虑线程同步、任务队列的管理等问题,以确保线程池的稳定性和性能。 ### 回答3: 线程池是用来管理和复用线程的一种机制,可以更有效地使用系统资源和提高应用程序的性能。下面是使用C语言实现线程池的一般步骤: 1. 定义一个线程池的结构体,包含线程池的状态、大小、最大线程数、工作任务队列等信息。 ```c typedef struct { pthread_t *threads; // 线程数组 int thread_count; // 线程数 int max_threads; // 最大线程数 int pool_size; // 线程池大小 int shutdown; // 关闭标志 pthread_mutex_t mutex; // 互斥锁 pthread_cond_t notify; // 条件变量 Task *task_queue; // 任务队列 } ThreadPool; ``` 2. 初始化线程池,创建指定数量的线程。 ```c ThreadPool* thread_pool_init(int pool_size) { ThreadPool *pool = malloc(sizeof(ThreadPool)); pool->threads = malloc(pool_size * sizeof(pthread_t)); pool->thread_count = pool_size; pool->max_threads = pool_size; pool->pool_size = 0; pool->shutdown = 0; // 初始化互斥锁和条件变量 pthread_mutex_init(&(pool->mutex), NULL); pthread_cond_init(&(pool->notify), NULL); // 创建线程 for (int i = 0; i < pool_size; i++) { pthread_create(&(pool->threads[i]), NULL, thread_worker, (void*)pool); } return pool; } ``` 3. 定义线程工作函数,不断从任务队列中取出任务执行。 ```c void* thread_worker(void *arg) { ThreadPool *pool = (ThreadPool*)arg; while (1) { pthread_mutex_lock(&(pool->mutex)); // 线程池关闭,退出线程 while (pool->pool_size == 0 && !pool->shutdown) { pthread_cond_wait(&(pool->notify), &(pool->mutex)); } if (pool->shutdown) { pthread_mutex_unlock(&(pool->mutex)); pthread_exit(NULL); } // 从任务队列中取出任务执行 Task *task = pool->task_queue; pool->task_queue = pool->task_queue->next; pool->pool_size--; pthread_mutex_unlock(&(pool->mutex)); task->func(task->arg); free(task); } pthread_exit(NULL); } ``` 4. 定义任务结构体,包含任务函数指针和参数。 ```c typedef struct Task { void (*func)(void*); void *arg; struct Task *next; } Task; ``` 5. 向线程池中添加任务。 ```c void thread_pool_add_task(ThreadPool *pool, void (*func)(void*), void *arg) { Task *task = malloc(sizeof(Task)); task->func = func; task->arg = arg; task->next = NULL; pthread_mutex_lock(&(pool->mutex)); if (pool->task_queue == NULL) { pool->task_queue = task; } else { Task *cur = pool->task_queue; while (cur->next != NULL) { cur = cur->next; } cur->next = task; } pool->pool_size++; pthread_mutex_unlock(&(pool->mutex)); pthread_cond_signal(&(pool->notify)); } ``` 6. 关闭线程池。 ```c void thread_pool_shutdown(ThreadPool *pool) { if (pool == NULL) { return; } pthread_mutex_lock(&(pool->mutex)); pool->shutdown = 1; pthread_mutex_unlock(&(pool->mutex)); pthread_cond_broadcast(&(pool->notify)); // 等待线程退出 for (int i = 0; i < pool->thread_count; i++) { pthread_join(pool->threads[i], NULL); } // 释放资源 free(pool->threads); while (pool->task_queue != NULL) { Task *next = pool->task_queue->next; free(pool->task_queue); pool->task_queue = next; } pthread_mutex_destroy(&(pool->mutex)); pthread_cond_destroy(&(pool->notify)); free(pool); } ``` 以上是一个简单的线程池实现,通过初始化线程池、添加任务、关闭线程池等操作,可以有效地管理和复用线程,提高应用程序的性能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值