基于链表的线程池----增加线程退出以及扩容和减容

之前在20 Linux线程池 一文中曾经实现过基于环形队列的线程池。
但是并不是很完美,因为并没有实现子线程退出的功能,子线程只能随着进程一块退出,并且扩容和增容也没有实现。

本文中的基于链表的线程池对线程池进行了进一步封装,解决了这些问题。


总体结构

在这里插入图片描述

该线程池一共有三个类型:

  1. 任务类,也就是要执行的回调函数、以及执行回调函数需要的数据。
//任务节点
struct nJob
{
    //回调函数,用来处理不同的任务
    void (*job_function)(nJob *job);
    void *user_data;
    //前后指针,使得节点形成链表
    nJob *prev;
    nJob *next;
};
  1. 工作类,该类型中有一个线程池的指针,能通过线程池找到任务类,从而获取任务然后执行。
class nThreadPool;
//执行队列节点
struct nWorker
{

    //线程的id
    pthread_t thread = 0;
    //判断是否终止退出
    bool isexit = false;
    //指向线程池
    nThreadPool *workqueue = nullptr;
    nWorker *prev = nullptr;
    nWorker *next = nullptr;
};
  1. 线程池类,该类型主要负责管理和控制任务类和工作类,并且通过互斥锁和信号量,保证工作类的调度运行。
class nThreadPool
{
private:
    //链表头插
    template <class T>
    static void addListNode(T *&item, T *&list);
    //从链表中删除某个节点
    template <class T>
    static void eraseListNode(T *&item, T *&list);

    //任务队列和执行队列
    nWorker *workers;
    nJob *waiting_jobs;
    pthread_mutex_t jobs_mtx;
    pthread_cond_t jobs_cond;
    //当前任务队列中任务的数量
    int jobs_num = 0;
    //当前执行队列中线程的数量
    atomic<int> worker_num;
//最大线程的数量
#define max_worker_num 20

    //销毁线程池若干个节点
    void deleteThreadPoolShutdown();
    void eraseTthrad(int del_num);
    void increaseThread(int numWorkers);
public:
    //是否需要增加或者减少线程数量
    void isChange();
    ~nThreadPool()
    {
        deleteThreadPoolShutdown();
    }
    //回调函数
    static void *callbackWorkerThread(void *ptr);
    //创建线程池
    int initThreadPoolCreate(int numWorkers);
    //加入任务
    void addThreadPoolQueue(nJob *job);
    bool isEmpty();
    int getJobNums();
    atomic<int> &getWorkerNums();
};

void func(nJob *job)
{

    int index = *(int *)job->user_data;

    std::cout << "thread is[" << pthread_self() << "]," << index << " pow is:" << pow(index, 2) << std::endl;

    free(job->user_data);
    free(job);
    sleep(1);
}

int main(int argc, char *argv[])
{
    clock_t begin = clock();

    nThreadPool *pool = new nThreadPool;

    pool->initThreadPoolCreate(5);

    int i = 0;
    for (i = 0; i < 1000; i++)
    {
        nJob *job = (nJob *)malloc(sizeof(nJob));
        if (job == NULL)
        {
            perror("malloc");
            exit(1);
        }
        job->job_function = func;
        job->user_data = malloc(sizeof(int));
        *(int *)job->user_data = i;
        pool->addThreadPoolQueue(job);
    }
    while (!pool->isEmpty())
    {
        // cout << "当前线程中任务节点数量:" << pool->getJobNums() << endl;
        pool->isChange();
        sleep(1);
    }
    delete pool;
    sleep(3);
    clock_t end = clock();
    cout << "总用时:" << ((double)end - (double)begin) / CLOCKS_PER_SEC * 1000 << "ms" << endl;
}

这里采用了伪删除的思路,每个工作节点有一个bool变量,当该变量为true时,则在下次获取任务之前就退出该线程。
在这里插入图片描述

有了这样的删除思路,剩下的增容和减容,也就好实现了。这里以增加一倍和减少一倍为例:
在这里插入图片描述


代码实现

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <iostream>
#include <unistd.h>
#include <time.h>
#include <atomic>
#include <cmath>
using namespace std;

class nThreadPool;
//执行队列节点
struct nWorker
{

    //线程的id
    pthread_t thread = 0;
    //判断是否终止退出
    bool isexit = false;
    //指向线程池
    nThreadPool *workqueue = nullptr;
    nWorker *prev = nullptr;
    nWorker *next = nullptr;
};

//任务节点
struct nJob
{
    //回调函数,用来处理不同的任务
    void (*job_function)(nJob *job);
    void *user_data;
    //前后指针,使得节点形成队列
    nJob *prev = nullptr;
    nJob *next = nullptr;
};

class nThreadPool
{
private:
    //链表头插
    template <class T>
    static void addListNode(T *&item, T *&list)
    {
        item->prev = nullptr;
        item->next = list;
        if (list != nullptr)
        {
            list->prev = item;
        }
        list = item;
    }
    //从链表中删除某个节点
    template <class T>
    static void eraseListNode(T *&item, T *&list)
    {
        if (item->prev != nullptr)
            item->prev->next = item->next;
        if (item->next != nullptr)
            item->next->prev = item->prev;
        if (list == item)
        {
            list = item->next;
        }
        item->prev = item->next = nullptr;
    }

    //任务队列和执行队列
    nWorker *workers;
    nJob *waiting_jobs;
    pthread_mutex_t jobs_mtx;
    pthread_cond_t jobs_cond;
    //当前任务队列中任务的数量
    int jobs_num = 0;
    //当前执行队列中线程的数量
    atomic<int> worker_num;
//最大线程的数量
#define max_worker_num 20

    //销毁线程池若干个节点
    void deleteThreadPoolShutdown()
    {
        cout << "开始销毁线程池-------------------------" << endl;
        nWorker *worker = NULL;
        for (worker = this->workers; worker != NULL; worker = worker->next)
        {
            worker->isexit = true;
        }
        // pthread_mutex_lock(&this->jobs_mtx);
        this->workers = worker;
        pthread_cond_broadcast(&this->jobs_cond);
        // pthread_mutex_unlock(&this->jobs_mtx);
    }
    void eraseTthrad(int del_num)
    {
        nWorker *worker = NULL;
        for (worker = this->workers; worker != NULL && del_num != 0; worker = worker->next, del_num--)
        {
            worker->isexit = true;
        }
        this->workers = worker;
    }
    void increaseThread(int numWorkers)
    {
        //接下来开始创建执行队列(线程)
        int i = 0;
        for (i = this->worker_num; i < numWorkers; i++)
        {
            nWorker *worker = (nWorker *)malloc(sizeof(nWorker));
            if (worker == NULL)
            {
                perror("malloc");
                return;
            }

            memset(worker, 0, sizeof(nWorker));
            worker->workqueue = this;
            //创建线程,设置回调函数
            int ret = pthread_create(&worker->thread, NULL, callbackWorkerThread, (void *)worker);
            if (ret)
            {
                perror("pthread_create");
                free(worker);

                return;
            }
            //将执行的节点,放到执行队列中
            addListNode(worker, worker->workqueue->workers);
        }
        worker_num = numWorkers;
    }

public:
    //是否需要增加或者减少线程数量
    void isChange()
    {
        //如果任务的数量是线程数量的三倍,则增加一倍的线程
        if (this->jobs_num > this->worker_num * 3)
        {
            // cout << "开始扩容,work数量:" << this->worker_num << "任务数量:" << this->jobs_num << endl;
            increaseThread(this->worker_num * 2);
            // cout << "扩容成功,当前work数量:" << this->worker_num << endl;
        }
        //如果线程比最大线程数量要大,则减少一半的线程数量
        else if ((this->worker_num > max_worker_num))
        {
            // cout << "开始减容,work数量:" << this->worker_num << "最大数量:" << max_worker_num << endl;
            //减容
            eraseTthrad(this->worker_num / 2);
            this->worker_num = max_worker_num;
        }
    }
    ~nThreadPool()
    {
        deleteThreadPoolShutdown();
    }
    //回调函数
    static void *callbackWorkerThread(void *ptr)
    {
        nWorker *worker = (nWorker *)ptr;
        //不断去任务队列中取任务
        while (1)
        {
            //需要加锁,因为任务队列是临界资源
            pthread_mutex_lock(&worker->workqueue->jobs_mtx);
            //任务队列中没任务,等待
            while (worker->workqueue->waiting_jobs == NULL)
            {
                //当前执行队列的节点需要被释放了
                if (worker->isexit)
                {
                    break;
                }
                pthread_cond_wait(&worker->workqueue->jobs_cond, &worker->workqueue->jobs_mtx);
            }
            //当前执行队列的节点需要被释放了
            if (worker->isexit)
            {
                pthread_mutex_unlock(&worker->workqueue->jobs_mtx);
                break;
            }
            //取出要执行的节点,执行回调函数
            nJob *job = worker->workqueue->waiting_jobs;
            if (job != NULL)
            {
                eraseListNode(job, worker->workqueue->waiting_jobs);
                worker->workqueue->jobs_num--;
                pthread_mutex_unlock(&worker->workqueue->jobs_mtx);
            }
            else
            {
                pthread_mutex_unlock(&worker->workqueue->jobs_mtx);
                continue;
            }
            job->job_function(job);
        }
        //释放对应的执行节点
        eraseListNode(worker, worker->workqueue->workers);
        free(worker);
        // cout << "释放了一个线程" << endl;
        pthread_exit(NULL);
    }
    //创建线程池
    int initThreadPoolCreate(int numWorkers)
    {
        if (numWorkers < 1)
            numWorkers = 1;
        memset(this, 0, sizeof(nThreadPool));
        //初始化条件变量和锁
        pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER;
        memcpy(&this->jobs_cond, &blank_cond, sizeof(this->jobs_cond));
        pthread_mutex_t blank_mutex = PTHREAD_MUTEX_INITIALIZER;
        memcpy(&this->jobs_mtx, &blank_mutex, sizeof(this->jobs_mtx));

        //接下来开始创建执行队列(线程)
        int i = 0;
        for (i = 0; i < numWorkers; i++)
        {
            nWorker *worker = (nWorker *)malloc(sizeof(nWorker));
            if (worker == NULL)
            {
                perror("malloc");
                return 1;
            }

            memset(worker, 0, sizeof(nWorker));
            worker->workqueue = this;
            //创建线程,设置回调函数
            int ret = pthread_create(&worker->thread, NULL, callbackWorkerThread, (void *)worker);
            if (ret)
            {
                perror("pthread_create");
                free(worker);

                return 1;
            }
            //将执行的节点,放到执行队列中
            addListNode(worker, worker->workqueue->workers);
        }
        worker_num = numWorkers;
        return 0;
    }
    //加入任务
    void addThreadPoolQueue(nJob *job)
    {

        pthread_mutex_lock(&this->jobs_mtx);

        addListNode(job, this->waiting_jobs);
        this->jobs_num++;
        pthread_cond_signal(&this->jobs_cond);
        pthread_mutex_unlock(&this->jobs_mtx);
    }
    bool isEmpty()
    {
        return this->jobs_num == 0;
    }
    int getJobNums()
    {
        return this->jobs_num;
    }
    atomic<int> &getWorkerNums()
    {
        return this->worker_num;
    }
};

void func(nJob *job)
{

    int index = *(int *)job->user_data;

    std::cout << "thread is[" << pthread_self() << "]," << index << " pow is:" << pow(index, 2) << std::endl;

    free(job->user_data);
    free(job);
    sleep(1);
}

int main(int argc, char *argv[])
{
    clock_t begin = clock();

    nThreadPool *pool = new nThreadPool;

    pool->initThreadPoolCreate(5);

    int i = 0;
    for (i = 0; i < 1000; i++)
    {
        nJob *job = (nJob *)malloc(sizeof(nJob));
        if (job == NULL)
        {
            perror("malloc");
            exit(1);
        }
        job->job_function = func;
        job->user_data = malloc(sizeof(int));
        *(int *)job->user_data = i;
        pool->addThreadPoolQueue(job);
    }
    while (!pool->isEmpty())
    {
        // cout << "当前线程中任务节点数量:" << pool->getJobNums() << endl;
        pool->isChange();
        sleep(1);
    }
    delete pool;
    sleep(3);
    clock_t end = clock();
    cout << "总用时:" << ((double)end - (double)begin) / CLOCKS_PER_SEC * 1000 << "ms" << endl;
}

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

今天也要写bug、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值