仿写linux线程池

项目描述

线程池是一种多线程处理形式,首先将任务添加到任务队列,然后在创建线程后自动启动任务。每个线 程都是默认的大小和优先级。如果线程某个线程空闲(如有等待事件),那么线程池将会插入其他辅助线 程使服务器保持繁忙。线程池中的线程不会超过最大数,如果线程池中的线程都处于一个忙状态,则新 来的线程则需要排队等候。

设计思路

  1. 创建任务队列,存储待处理的任务

将待处理的任务添加到任务队列,将已经处理的任务从队列中移除

  1. 实现工作中的线程,读任务队列并处理

打工人线程的任务就是不停的读任务队列,从队列中取出任务并处理,如果任务队列为空,工作的线程将会被阻塞,等到有了新的任务才能解除阻塞。

  1. 实现管理者线程,对线程池的状态进行检测和维护

管理者线程的任务就是一直检查任务队列中的任务数量,任务多的时候,就添加部分打工人线程,任务少的时候就减少一部分。

主要的功能模块

数据结构(任务队列类和线程池类)


#pragma once
#include <queue>
#include <pthread.h>
using callback = void (*) (void* arg);//函数指针

//任务结构体
struct Task
{
    Task()
    {
        function = nullptr;
        arg = nullptr;
    }

    Task(callback f, void* arg)
    {
        this->arg = arg;
        function = f;
    }

    callback function;
    void* arg;//普通指针
};

class TaskQueue
{
public:
    TaskQueue();
    ~TaskQueue();

    //添加任务
    void addTask(Task task);
    void addTask(callback f, void* arg);
    //取出一个任务
    Task takeTask();

    //获取当前任务个数(内联函数提高效率)
    inline size_t taskNumber()
    {
        return m_taskQ.size();
    }


private:
    pthread_mutex_t m_mutex;
    std::queue<Task> m_taskQ;
};

#include "TaskQueue.h"
//#include "TaskQueue.cpp"
class ThreadPool
{
public:
    //创建线程池并初始化
    ThreadPool(int min, int max);

    //销毁线程池
    ~ThreadPool();

    //给线程池添加任务
    void addTask(Task task);

    //获取线程池中工作的线程个数
    int getBusyNum();

    //获取线程池中活着的线程个数
    int getAliveNum();


private:

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


private:
    //任务队列
    TaskQueue* taskQ;

    pthread_t managerID;    //管理者线程ID
    pthread_t* threadIDs;   //工作的线程ID
    int minNum;             //最小线程数
    int maxNum;             //最大线程数
    int busyNum;            //忙的线程个数
    int liveNum;            //存活的线程个数
    int exitNum;            //要销毁的线程个数
    pthread_mutex_t mutexPool;//锁整个线程池
    pthread_cond_t notEmpty;//任务队列是否空了
    static const int NUMBER = 2;//依次添加两个线程

    bool shutdown;           //是否销毁线程池,销毁为1


};
  1. 创建线程池并初始化

创建任务队列,一个管理者线程和若干打工人线程并初始化id为0.


ThreadPool::ThreadPool(int min, int max)
{
    //实例化任务队列
    taskQ = new TaskQueue;

    if (taskQ == nullptr)
    {
        printf("new taskQ fail...\n");
        return;
    }


    do { 
        threadIDs = new pthread_t[max]; //new一个工作者线程id数组

        if (threadIDs == nullptr)
        {
            printf("new threadpool fail...\n");
            break;
        }

        memset(threadIDs, 0, sizeof(pthread_t) * max);
        //线程id初始化为0

        minNum = min;
        maxNum = max;
        busyNum = 0;
        liveNum = min;  //和最小个数相等
        exitNum = 0;

        if(pthread_mutex_init(&mutexPool, NULL) != 0 || pthread_cond_init(&notEmpty, NULL) != 0)
        {
            std::cout<<"mutex or condition init fail..."<<endl;
            break;
        }

        shutdown = false;

        //创建一个管理者线程和若干工作者线程
        pthread_create(&managerID, NULL, manager, this);

        for (int i = 0; i < min; i++)
        {
            pthread_create(&threadIDs[i], NULL, worker, this);
        }

        return;

    } while (0);

    //释放资源
    if (threadIDs)  delete[]threadIDs;
    if (taskQ)  delete taskQ;


}
  1. 管理者线程

根据队列中的线程数判断是否添加/减少线程


void* ThreadPool::manager(void* arg)
{
    ThreadPool* pool = static_cast<ThreadPool*>(arg);

    while (!pool->shutdown)//shutdown等于0的时候开始干活
    {
        //每隔3秒检测一次,检测线程池是否被关闭
        sleep(3);

        //取出线程池中任务数量和当前线程数量
        pthread_mutex_lock(&pool->mutexPool);
        size_t queueSize = pool->taskQ->taskNumber();
        int liveNum = pool->liveNum;  //  取出存活线程的数量
        int busyNum = pool->busyNum;
        pthread_mutex_unlock(&pool->mutexPool);


        //什么时候添加线程(干活人少了)
        //任务的个数>存活的线程个数 && 存活的线程数<最大的线程数,否则不能继续添加新线程
        if (queueSize > liveNum && liveNum < pool->maxNum)
        {
            pthread_mutex_lock(&pool->mutexPool);
            int counter = 0;
            for (int i = 0; i < pool->maxNum && counter < NUMBER && pool->liveNum < pool->maxNum; i++)
            {
                if (pool->threadIDs[i] == 0)//寻找没有存储线程id
                {
                    pthread_create(&pool->threadIDs[i], NULL, worker, pool);//把线程id放在数组里
                    counter++;
                    pool->liveNum++;
                }
            }
            pthread_mutex_unlock(&pool->mutexPool);
        }

        //销毁线程
        //忙的线程*2<存活的线程数 && 存活的线程数>最小线程数    存活的线程很多并且干活的线程很少
        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 nullptr;
}

3.打工人线程


void* ThreadPool::worker(void* arg)
{
    //传进来的其实是threadpool的实例对象,需对arg进行类型转换成threadpool类型
    ThreadPool* pool = static_cast<ThreadPool*>(arg);

    while (true)//线程进入循环,从线程池中不停的取任务
    {
        pthread_mutex_lock(&pool->mutexPool);
        //当前队列是否为空并且没有被关闭
        while (pool->taskQ->taskNumber() == 0 && !pool->shutdown)
        {
            //阻塞工作线程,notempty记录了哪些线程被阻塞,mutexpool同时被解开
            pthread_cond_wait(&pool->notEmpty, &pool->mutexPool);

            //判断是否要销毁线程
            if (pool->exitNum > 0)  //工作的线程醒了,这里是管理者线程要做的会给exitnum初始化
            {
                pool->exitNum--;
                if (pool->liveNum > pool->minNum)
                {
                    pool->liveNum--;
                    pthread_mutex_unlock(&pool->mutexPool);
                    pool->threadExit();
                }
            }
        }

        //判断线程池是否被关闭
        if (pool->shutdown)
        {
            //关闭了则打开互斥锁,退出当前的线程
            pthread_mutex_unlock(&pool->mutexPool);
            pool->threadExit();
        }

        //从任务队列中取任务
        Task task = pool->taskQ->takeTask();

        //移动头结点

        
        //解锁
        pool->busyNum++;
        pthread_mutex_unlock(&pool->mutexPool);//解锁后其他线程就可以访问线程池里的数据

        printf("thread %ld start working...\n", pthread_self());


        task.function(task.arg);
        delete task.arg;
        task.arg = nullptr;

        printf("thread %ld end workong...\n", pthread_self());

        pthread_mutex_lock(&pool->mutexPool);
        pool->busyNum--;
        pthread_mutex_unlock(&pool->mutexPool);
    }
    return nullptr;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值