线程池功能、原理以及C++线程池的极简实现

线程池功能、原理以及C++线程池的极简实现

博主写博客最讨厌写的很麻烦了,博主会抛弃很多错误判断、一些不涉及主体的功能,让读者很容易读懂原理,读者可以根据博主的代码进行时间复杂度优化以及代码优化,毕竟是教学博主,也不喜欢秀技啦,秀的读者看不懂就偏移教学的本质啦。还想看啥的可以评论博主,博主来给你们写博客

线程池的功能

  • 线程在创建和销毁的时候会消耗大量的时间,也加大了编码的难度,线程池是将一个个线程函数变成了任务,这些任务会加入队列,线程池会一个个的取出任务去做处理,这样的话会节省大量的不必要消耗,也能减少编码难度

线程池的原理

基本数据结构
using thrdpool_t_func_ptr = void* (*)(void*);		//任务函数指针
class func {
public:
    func() {
        key = func_key_auto++;
        func_state = 0;
    }
public:
    //任务函数
    thrdpool_t_func_ptr exec_func;
    //函数参数
    void* arg;
    //0为未执行,1 为正在执行,2 为正常退出
    short func_state;
    //函数返回值
    void* returnValue;
    //函数唯一识别码
    long long key;
    //函数识别码创建
    static std::atomic<long long> func_key_auto;
};
using func_t = func;		//线程状态
  1. 构造函数创建指定数量线程,并且线程一直执行thrdExecFunc函数,thrdExecFunc在第三步骤有展示

    ThrdPool::ThrdPool(int n) {
        if (n < 1)n = 1;
        size = n;
        exec_length = 0;
        exec_func_num = 0;
        thrd_arr = new pthread_t[n];
        //创建空转函数实例
        for (int i = 0;i < n;i++) {
            pthread_create(&thrd_arr[i], nullptr, thrdExecFunc, this);
        }
    }
    
  2. 通过调用添加任务接口向线程池加入任务,任务接口格式如下

func_t* thrdpoolPost(thrdpool_t_func_ptr func_p, void* _arg);
  1. 线程池在无任务状态下运行下面的函数,对队列加锁后取出任务的函数指针并加以运行,若无任务存在则挂起线程 5000 纳秒

    void* ThrdPool::thrdExecFunc(void* data) {
        ThrdPool* p = (ThrdPool*)data;      //data 为 this 指针
        while (true) {
            p->k.lock();    //加锁
            if (!p->funcQueue.empty()) {        //当队列不为空时
                func_t* func_exec = p->funcQueue.front();   //从队列顶部取出一个函数指针(任务)
                p->funcQueue.pop();     //弹出队列的函数指针
                func_exec->func_state = 1;  //将任务状态改为执行中
                p->exec_func_num++;         //正在执行的任务量+1    exec_func_num是原子变量就不加锁了
                p->k.unlock();              //解锁
                func_exec->returnValue = func_exec->exec_func(func_exec->arg);  //将函数返回值加入 func_exec
                p->k.lock();                //加锁
                func_exec->func_state = 2;  //将任务状态改为执行结束
                p->funcList.push_back(func_exec);   //将任务状态加入列表中,可以根据此状态获取返回值
                p->k.unlock();              //解锁
                p->exec_func_num--;         //正在执行任务量-1
            }
            else {      //当队列为空时
                p->k.unlock();  //解锁
                usleep(5000);   //等待 5000 纳秒防止 CPU 空转
            }
        }
        return nullptr;
    }
    
  • 以上的线程池的基本原理,不懂的小伙伴可以多看两遍

下面是完整代码展示

  • 目录结构

    .
    ├── CMakeLists.txt
    ├── build
    └── src
        ├── main.cpp
        ├── thrdpool.cpp
        └── thrdpool.h
    
  • 编译、执行命令

    cd build
    cmake ..
    make
    ./thedpool
    
  • CMakeLists.txt

    cmake_minimum_required(VERSION 3.10)
    project(main)
    set(CMAKE_CXX_STANDARD 11)
    add_executable(thedpool ./src/main.cpp
                            ./src/thrdpool.cpp)
    
  • thrdpool.h

    #ifndef SRC_KCEP_THRDPOOL
    #define SRC_KCEP_THRDPOOL
    #include<queue>
    #include<vector>
    #include<pthread.h>
    #include <atomic>
    #include <mutex>
    #include <unistd.h>
    class func;
    using thrdpool_t_func_ptr = void* (*)(void*);
    using func_t = func;
    class func {
    public:
        func() {
            key = func_key_auto++;
            func_state = 0;
        }
    public:
        //任务函数
        thrdpool_t_func_ptr exec_func;
        //函数参数
        void* arg;
        //0为未执行,1 为正在执行,2 为正常退出
        short func_state;
        //函数返回值
        void* returnValue;
        //函数唯一识别码
        long long key;
        //函数识别码创建
        static std::atomic<long long> func_key_auto;
    };
    
    class ThrdPool {
    public:
        //创建一个线程池并指定线程池线程数量
        ThrdPool(int n);
        //析构
        ~ThrdPool();
        //删除线程池,内置线程和任务将会被强制清除
        void thrdpoolStopAll();
        //向任务队列压入一个任务函数,此任务队列大小可比线程数量高
        func_t* thrdpoolPost(thrdpool_t_func_ptr func_p, void* _arg);
        //等待一个线程池所有任务执行结束
        void thrdpool_join();
        //获取函数返回值
        void* getFuncValue(func_t* p);
        //线程池正在执行的线程数量
        int getExecLength();
        //线程池当前队列任务量
        int getQueueLength();
    private:
        //线程池空转函数,等待任务队列有任务提出执行
        static void* thrdExecFunc(void* data);
        //待执行队列
        std::queue<func_t*> funcQueue;
        //执行完毕列表,可从此列表获取返回值
        std::vector<func_t*> funcList;
        //正在执行的数量
        int exec_length;
        //最大线程数量
        int size;
        //线程句柄
        pthread_t* thrd_arr;
        //锁
        std::mutex k;
        //正在执行任务的线程数量
        std::atomic<int> exec_func_num;
    };
    #endif
    
  • thrdpool.cpp

    #include "thrdpool.h"
    #include<iostream>
    std::atomic<long long> func::func_key_auto = { 0 };
    ThrdPool::ThrdPool(int n) {
        if (n < 1)n = 1;
        size = n;
        exec_length = 0;
        exec_func_num = 0;
        thrd_arr = new pthread_t[n];
        //创建空转函数实例
        for (int i = 0;i < n;i++) {
            pthread_create(&thrd_arr[i], nullptr, thrdExecFunc, this);
        }
    }
    
    ThrdPool::~ThrdPool() {
        while (!funcQueue.empty()) {
            delete funcQueue.front();
            funcQueue.pop();
        }
        for (auto i : funcList) {
            delete i;
            i = nullptr;
        }
        delete[] thrd_arr;
        thrd_arr = nullptr;
    }
    void ThrdPool::thrdpoolStopAll() {
        for (int i = 0;i < size;i++) {
            //强行注销线程
            pthread_cancel(thrd_arr[i]);
            //将已被注销的线程设置为无主线程
            pthread_detach(thrd_arr[i]);
        }
        while (!funcQueue.empty()) {
            delete funcQueue.front();
            funcQueue.pop();
        }
        for (auto i : funcList) {
            delete i;
            i = nullptr;
        }
        delete[] thrd_arr;
        thrd_arr = nullptr;
    }
    func_t* ThrdPool::thrdpoolPost(thrdpool_t_func_ptr func_p, void* _arg) {
        func_t* func_exec = new func_t;
        func_exec->exec_func = func_p;
        func_exec->arg = _arg;
        k.lock();
        funcQueue.push(func_exec);
        k.unlock();
        return func_exec;
    }
    
    void ThrdPool::thrdpool_join() {
        while (true) {
            k.lock();
            if (exec_func_num == 0 && funcQueue.size() == 0) {
                k.unlock();
                break;
            }
            else {
                k.unlock();
                usleep(1000);
            }
        }
    }
    
    void* ThrdPool::getFuncValue(func_t* p) {
        if (p->func_state != 2) {
            std::cerr << "\033[1;31m" << "Error:" << "\033[0m" << "无对应返回值,线程可能未执行或者未结束" << std::endl;
            return nullptr;
        }
        return p->returnValue;
    }
    
    int ThrdPool::getExecLength() {
        return exec_func_num;
    }
    
    int ThrdPool::getQueueLength() {
        return funcQueue.size();
    }
    
    void* ThrdPool::thrdExecFunc(void* data) {
        ThrdPool* p = (ThrdPool*)data;      //data 为 this 指针
        while (true) {
            p->k.lock();    //加锁
            if (!p->funcQueue.empty()) {        //当队列不为空时
                func_t* func_exec = p->funcQueue.front();   //从队列顶部取出一个函数指针(任务)
                p->funcQueue.pop();     //弹出队列的函数指针
                func_exec->func_state = 1;  //将任务状态改为执行中
                p->exec_func_num++;         //正在执行的任务量+1    exec_func_num是原子变量就不加锁了
                p->k.unlock();              //解锁
                func_exec->returnValue = func_exec->exec_func(func_exec->arg);  //将函数返回值加入 func_exec
                p->k.lock();                //加锁
                func_exec->func_state = 2;  //将任务状态改为执行结束
                p->funcList.push_back(func_exec);   //将任务状态加入列表中,可以根据此状态获取返回值
                p->k.unlock();              //解锁
                p->exec_func_num--;         //正在执行任务量-1
            }
            else {      //当队列为空时
                p->k.unlock();  //解锁
                usleep(5000);   //等待 5000 微秒防止 CPU 空转
            }
        }
        return nullptr;
    }
    
  • main.cpp

    #include"thrdpool.h"
    #include<iostream>
    using namespace std;
    void* func(void* p){
        cout<<*((int*)(p))<<endl;
        int* s=new int;
        *s=234;
        delete p;
        return s;
    }
    int main(){
        ThrdPool p(2);
        auto i=p.thrdpoolPost(func,new int(999));
        p.thrdpool_join();
        cout<<*((int*)p.getFuncValue(i))<<endl;
        delete p.getFuncValue(i);
        usleep(5000);
    }
    
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值