菜鸟笔记
提升您的技术认知

面试题

实现一个LRU Cache 算法LRU Cache C++三种解法java实现LRU算法及编码实现LRU策略缓存LRU算法常见缓存算法和LRU的c++实现设计循环双端队列(deque)LRU 缓存结构 (c++ 哈希双链表实现)LRU缓存机制删除单链表中的指定节点Linux 内核经典面试题拼多多社招面经:Redis是重点,讲一讲redis的内存模型线程、进程、协程的区别C++经典面试题面试官:我们只想要这样的C++工程师Linux C/C++ 学习路线链表操作汇总C++11的智能指针面试题浏览器中输入url后发生的事情常用的限流算法HTTP协议和HTTPS协议面试题网络编程面试题目总结c++后台面试题目如何实现LRU算法?如何寻找无序数组中的第K大元素?布隆过滤器 - 如何在100个亿URL中快速判断某URL是否存在?如何实现大整数相加?C++面试题及基本知识点总结C++给定出栈序列判定是否合法消息队列面试题要点redis缓存击穿,失效以及热点key解决方案网页在浏览器上的渲染过程几种限流算法lru算法例题C/C++常见面试知识点总结附面试真题----20210529更新引入MQ消息队列的作用及其优缺点MySQL面试篇社招三年后端面试题60道测开面试题,背完直接涨工资二叉树的层序遍历(两种方法实现)Bitmap 海量数据处理字符串倒序输出的五种方法C语言 输入10个数,统计出并输出正数、负数和0的个数字节三面:如何设计一个高并发系统架构,网络 面试36问,DDos攻击原理C++线程池使用 C++11 编写可复用多线程任务池

使用 C++11 编写可复用多线程任务池

阅读 : 764

类的功能

  • Task (任务基类)
    该类主要实现一个任务类
    virtual int doWork() = 0;

  • TaskQueue (任务队列)
    该类主要针对任务的存储、删除、撤回等状态做管理

  • ThreadPool (线程池)
    整个线程池的核心业务处理类

代码

  • Task.h
//任务的基类
#pragma once

#include <time.h>
#include <atomic>

//任务的基类
class Task
{
public:
    //构造、析构函数
    Task():_id(_nRequestID++),_isCancelRequired(false),_createTime(clock()){}
    ~Task(){};

    // 任务类虚接口,继承这个类的必须要实现这个接口
    virtual int doWork(void) = 0;

    // 任务已取消回调
    virtual int onCanceled(void)
    {
        return  1;
    }
    // 任务已完成
    virtual int onCompleted(int)
    {
        return 1;
    }
    // 任务是否超时
    virtual bool isTimeout(const clock_t& now)
    {
        return ((now - _createTime) > 5000);
    }
    // 获取任务ID
    size_t getID(void)
    {
        return _id;
    }
    //获取任务取消状态
    bool isCancelRequired(void)
    {
        return _isCancelRequired;
    }
    //设置任务取消状态
    void setCancelRequired(void)
    {
        _isCancelRequired = true;
    }



protected:
    size_t _id; //任务的唯一标识
    clock_t _createTime;    //任务创建时间,非Unix时间戳

private:
    static std::atomic<size_t> _nRequestID;
    std::atomic<bool> _isCancelRequired;    //任务取消状态
};

//selectany可以让我们在.h文件中初始化一个全局变量而不是只能放在.cpp中。
//这样的代码来初始化这个全局变量。既是该.h被多次include,链接器也会为我们剔除多重定义的错误。
__declspec(selectany) std::atomic<size_t> Task::_nRequestID = 100000;

  • TaskQueue.h
#pragma once

#include <deque>
#include <mutex>
#include <condition_variable>
#include <unordered_map>
#include <memory>
#include <thread>

//任务队列
template<typename T>
class TaskQueue
{
public:
    //向队列的末尾插入任务,task是任务类
    void put_back(std::shared_ptr<T>& task)
    {
        std::unique_lock<std::mutex> lock(_mutexQueue);
        _queue.push_back(task);
        _conditPut.notify_one();
    }

    //向队列的头部插入任务
    void put_front(std::shared_ptr<T>& task)
    {
        std::unique_lock<std::mutex> lock(_mutexQueue);
        _queue.push_front(task);
        _conditPut.notify_one();
    }

    //获取队首(并将任务加到运行任务列表中),返回tase是任务类
    std::shared_ptr<T> get(void) {
        std::unique_lock<std::mutex> lock(_mutexQueue);
        if (_queue.empty())
            return nullptr;
        //lock_guard取代了mutex的lock()和unlock();
        std::lock_guard<std::mutex> lock_doing_task(_mutexDoingTask);

        std::shared_ptr<T>& task = _queue.front();

        _mapDoingTask.insert(std::make_pair(task->getID(), task));

        _queue.pop_front();
        return task;
    }

    //获取双向链表queue的大小
    size_t size(void)
    {
        std::unique_lock<std::mutex> lock(_mutexQueue);
        return _queue.size();
    }

    //释放队列
    void release(void)
    {
        deleteAllTasks();
        _conditPut.notify_all();
    }

    //删除任务(从就绪队列删除,如果就绪队列没有,则看执行队列有没有,有的话置下取消状态位)
    int deleteTask(size_t nID)
    {
        std::unique_lock<std::mutex> lock(_mutexQueue, std::defer_lock);
        lock.lock();

        auto it = _queue.begin();
        for (; it != _queue.end(); ++it) 
        {
            if ((*it)->getID() == nID) 
            {
                _queue.erase(it);
                lock.unlock();
                return 0;
            }
        }
        //下面的逻辑可能会造成死锁,这里要及时释放
        lock.unlock();

        // 试图取消正在执行的任务
        {
            std::lock_guard<std::mutex> lock_doing_task(_mutexDoingTask);

            auto it_map = _mapDoingTask.find(nID);
            if (it_map != _mapDoingTask.end())
                it_map->second->setCancelRequired();
        }

        //任务执行完后再返回
        while (_mapDoingTask.count(nID))
            std::this_thread::sleep_for(std::chrono::milliseconds(20));

        return 0;
    }



    //删除所有任务
    int deleteAllTasks(void)
    {
        std::unique_lock<std::mutex> lock(_mutexQueue, std::defer_lock);
        lock.lock();

        if (!_queue.empty())
            _queue.clear();//清空

        {
            std::lock_guard<std::mutex> lock_doing_task(_mutexDoingTask);
            if (!_mapDoingTask.empty()) 
            {
                auto it_map = _mapDoingTask.begin();
                for (; it_map != _mapDoingTask.end(); ++it_map)
                    it_map->second->setCancelRequired();
            }
        }

        lock.unlock();

        //任务执行完后再返回
        while (!_mapDoingTask.empty())
            std::this_thread::sleep_for(std::chrono::milliseconds(50));
        return 0;
    }

    //任务完成回调(从运行列表中删除指定任务)
    int onTaskFinished(size_t nID)
    {
        std::lock_guard<std::mutex> lock_doing_task(_mutexDoingTask);
        auto it_map = _mapDoingTask.find(nID);
        if (it_map != _mapDoingTask.end())
            _mapDoingTask.erase(it_map);
        return 0;
    }

    //判断任务是否执行完毕
    std::shared_ptr<T> isTaskProcessed(size_t nId)
    {
        std::lock_guard<std::mutex> lock_queue(_mutexQueue);
        auto it = _queue.begin();
        for (; it != _queue.end(); ++it) {

            if ((*it)->getID() == nId)
                return *it;

        }
        std::lock_guard<std::mutex> lock_doing_task(_mutexDoingTask);

        auto it_map = _mapDoingTask.find(nId);
        if (it_map != _mapDoingTask.end())
            return it_map->second;
        return nullptr;
    }

    //等待有任务到达(带超时:超时自动唤醒)
    bool wait(std::chrono::milliseconds millsec)
    {
        std::unique_lock<std::mutex> lock(_mutexConditPut);
        _conditPut.wait_for(lock, millsec);
        return true;
    }

private:
    //就绪的任务
    std::mutex _mutexQueue;
    std::deque<std::shared_ptr<T>> _queue;
    //条件变量
    std::mutex _mutexConditPut;
    std::condition_variable _conditPut;

    //运行的任务
    std::mutex _mutexDoingTask;
    std::unordered_map<size_t, std::shared_ptr<T> > _mapDoingTask;

};

  • ThreadPool.h
#pragma once

#include <atomic>
#include <memory>
#include <mutex>
#include <iostream>
#include <thread>

#include "Task.h"
#include "TaskQueue.h"

class ThreadPool
{
public:
    // 线程池配置参数
    typedef struct tagThreadPoolConfig {
        int nMaxThreadsNum; // 最大线程数量
        int nMinThreadsNum; // 最小线程数量
        double dbTaskAddThreadRate; // 增 最大线程任务比 (任务数量与线程数量,什么比例的时候才加)
        double dbTaskSubThreadRate; // 减 最小线程任务比 (任务数量与线程数量,什么比例的时候才减)
    } ThreadPoolConfig;

public:
    //构造函数
    ThreadPool(void):_taskQueue(new TaskQueue<Task>()), _atcCurTotalThrNum(0), _atcWorking(true){}

    //析构函数
    ~ThreadPool(void)
    {
        release();
    }

    //初始化资源
    int init(const ThreadPoolConfig& threadPoolConfig) {
        // 错误的设置
        if (threadPoolConfig.dbTaskAddThreadRate < threadPoolConfig.dbTaskSubThreadRate)
            return 87;


        _threadPoolConfig.nMaxThreadsNum = threadPoolConfig.nMaxThreadsNum;
        _threadPoolConfig.nMinThreadsNum = threadPoolConfig.nMinThreadsNum;
        _threadPoolConfig.dbTaskAddThreadRate = threadPoolConfig.dbTaskAddThreadRate;
        _threadPoolConfig.dbTaskSubThreadRate = threadPoolConfig.dbTaskSubThreadRate;


        int ret = 0;
        // 创建线程池
        if (_threadPoolConfig.nMinThreadsNum > 0)
            ret = addProThreads(_threadPoolConfig.nMinThreadsNum);
        return ret;
    }

    // 添加任务
    int addTask(std::shared_ptr<Task> taskptr, bool priority=false)
    {
        const double& rate = getThreadTaskRate();
        int ret = 0;
        if (priority) 
        {
            if (rate > 1000)
                std::this_thread::sleep_for(std::chrono::milliseconds(1));
            _taskQueue->put_front(taskptr);
        }
        else 
        {
            // 检测任务数量
            if (rate > 100) {
                taskptr->onCanceled();
                return 298;
            }
            // 将任务推入队列
            _taskQueue->put_back(taskptr);

        }
        // 检查是否要扩展线程
        if (_atcCurTotalThrNum < _threadPoolConfig.nMaxThreadsNum
            && rate > _threadPoolConfig.dbTaskAddThreadRate)
            ret = addProThreads(1);
        return ret;
    }

    // 删除任务(从就绪队列删除,如果就绪队列没有,则看执行队列有没有,有的话置下取消状态位)
    int deleteTask(size_t nID)
    {
        return _taskQueue->deleteTask(nID);
    }

    // 删除所有任务
    int deleteAllTasks(void)
    {
        return _taskQueue->deleteAllTasks();
    }

    std::shared_ptr<Task> isTaskProcessed(size_t nId)
    {
        return _taskQueue->isTaskProcessed(nId);
    }

    // 释放资源(释放线程池、释放任务队列)
    bool release(void)
    {
        // 1、停止线程池。
        // 2、清楚就绪队列。
        // 3、等待执行队列为0
        releaseThreadPool();
        _taskQueue->release();

        int i = 0;
        while (_atcCurTotalThrNum != 0) 
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
            // 异常等待
            if (i++ == 10)
                exit(23);
        }

        _atcCurTotalThrNum = 0;
        return true;
    }

    // 获取当前线程任务比
    double getThreadTaskRate(void)
    {
        if (_atcCurTotalThrNum != 0)
            return _taskQueue->size() * 1.0 / _atcCurTotalThrNum;
        return 0;
    }
    // 当前线程是否需要结束
    bool shouldEnd(void)
    {

        bool bFlag = false;
        double dbThreadTaskRate = getThreadTaskRate();

        // 检查线程与任务比率
        if (!_atcWorking || _atcCurTotalThrNum > _threadPoolConfig.nMinThreadsNum
            && dbThreadTaskRate < _threadPoolConfig.dbTaskSubThreadRate)
            bFlag = true;

        return bFlag;
    }

    // 释放线程池
    bool releaseThreadPool(void)
    {
        _threadPoolConfig.nMinThreadsNum = 0;
        _threadPoolConfig.dbTaskSubThreadRate = 0;
        _atcWorking = false;
        return true;
    }


    // 添加指定数量的处理线程
    int addProThreads(int nThreadsNum)
    {
        try {
            for (; nThreadsNum > 0; --nThreadsNum)
                std::thread(&ThreadPool::taskProcessThread, this).detach();
        }
        catch (...){
            return 155;
        }

        return 0;
    }

    // 任务处理线程函数
    void taskProcessThread(void)
    {
        int nTaskProcRet = 0;
        // 线程增加
        _atcCurTotalThrNum.fetch_add(1);
        std::chrono::milliseconds mills_sleep(500);


        std::shared_ptr<Task> pTask;
        while (_atcWorking) 
        {
            // 从任务队列中获取任务
            pTask = _taskQueue->get();  //get会将任务添加到运行任务的map中去
            if (pTask == nullptr) 
            {
                if (shouldEnd())
                    break;

                // 进入睡眠池
                _taskQueue->wait(mills_sleep);
                continue;
            }

            // 检测任务取消状态
            if (pTask->isCancelRequired())
                pTask->onCanceled();
            else
                // 处理任务
                pTask->onCompleted(pTask->doWork());


            // 从运行任务队列中移除任务
            _taskQueue->onTaskFinished(pTask->getID());


            // 判断线程是否需要结束
            if (shouldEnd())
                break;

        }

        // 线程个数减一
        _atcCurTotalThrNum.fetch_sub(1);
    }

private:
    std::shared_ptr<TaskQueue<Task> > _taskQueue;   //任务队列
    ThreadPoolConfig _threadPoolConfig; //线程池配置
    std::atomic<bool> _atcWorking;  //线程池是否被要求结束
    std::atomic<int> _atcCurTotalThrNum;    //当前线程个数
};

  • FunTask.h
#pragma once

#include <functional>
#include "Task.h"

class FuncTask:public Task
{
public:
    FuncTask(std::function<int(void)> f) : _pf(f) {}
    FuncTask(void) : _pf(nullptr){}

    virtual ~FuncTask(){}

    template <typename F,typename... Args>
    void asynBind(F(*f)(Args...), Args... args)
    {
        _pf = std::bind(f, args...);
    }

    virtual int doWork()
    {
        if (_pf == nullptr)
            return 86;
        return _pf();
    }

private:
    typedef std::function<int(void)> pvFunc;
    pvFunc _pf;
};

  • main.cpp
#pragma once

#include <time.h>
#include <iostream>
#include <memory>
#include <string>
#include "ThreadPool.h"
#include "FuncTask.h"

using namespace std;

int vFunction(void)
{
    std::cout << __FUNCTION__ << std::endl;
    return 0;
}

int counter(int a,int b)
{
    std::cout << a << ":" << b << std::endl;
    return 0;
}


int main()
{

    ThreadPool::ThreadPoolConfig threadPoolConfig;
    threadPoolConfig.nMaxThreadsNum = 100;
    threadPoolConfig.nMinThreadsNum = 5;
    threadPoolConfig.dbTaskAddThreadRate = 3;
    threadPoolConfig.dbTaskSubThreadRate = 0.5;

    clock_t start = clock();
    {
        std::shared_ptr<ThreadPool> threadPool(new ThreadPool);
        threadPool->init(threadPoolConfig);

        int i = 1;
        while (true)
        {
            /*std::shared_ptr<FuncTask> request(new FuncTask(vFunction));
            threadPool->addTask(request);*/

            std::shared_ptr<FuncTask> request(new FuncTask);
            request->asynBind(counter, i++, 1);
            threadPool->addTask(request);
            if (request->getID() == 110000) {
                break;
            }
        }

        threadPool->release();
    }

    clock_t finish = clock();
    std::cout << "duration:" << finish - start << "ms" << std::endl;

    cout << "main:thread" << endl;
    return 0;
}