C++ 线程池实现和使用

 

复制代码
#ifndef MYTHREADPOLL_H
#define MYTHREADPOLL_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <vector>
#include <deque>
#include <thread>
#include <functional>
#include <mutex>
#include <sstream>
#include <condition_variable>
#include <iostream>
#include <string>
#include <atomic>
#include <future>

using namespace std;

typedef std::function<void()> task_t;

/**
 * @brief The MyThreadPoll class        自定义线程池
 */
class MyThreadPoll
{
public:
    MyThreadPoll(int threadNum=3);
    ~MyThreadPoll();

    /**
     * @brief addTask               向任务队列添加任务
     * @param task                  任务, 类似函数指针
     */
    void addTask(const task_t &task);

    //核心: 线程的主要工作函数,使用了后置返回类型,自动判断函数返回值(接受任何参数的任何函数)
    template<typename F, typename...Args>
    auto addTask(F&& f, Args&&... args) -> std::future<decltype(f(args...))>
    {
        // Create a function with bounded parameters ready to execute
        //
        printf("[ add Task ]\n");

        std::function<decltype(f(args...))()> func = std::bind(std::forward<F>(f), std::forward<Args>(args)...);//连接函数和参数定义,特殊函数类型,避免左、右值错误

        // Encapsulate it into a shared ptr in order to be able to copy construct // assign
        //封装获取任务对象,方便另外一个线程查看结果

        auto task_ptr = std::make_shared<std::packaged_task<decltype(f(args...))()>>(func);

        // Wrap packaged task into void function
        //利用正则表达式,返回一个函数对象

        std::function<void()> wrapper_func = [task_ptr]() {
            (*task_ptr)();
        };

        unique_lock<std::mutex> m_uniqueLock(m_mutex);
        // 队列通用安全封包函数,并压入安全队列
        m_taskQueue.push_back(wrapper_func);

        // 唤醒一个等待中的线程
        m_conditonVar.notify_one();

        // 返回先前注册的任务指针
        return task_ptr->get_future();          // std::future
    }

private:
    // 禁止拷贝复制和赋值
    MyThreadPoll(MyThreadPoll &) = delete;
    MyThreadPoll &operator=(const MyThreadPoll &my) = delete;
    /**
     * @brief threadLoop            线程循环
     */
    void threadLoop();

    /**
     * @brief start                 启动线程
     */
    void start();

    /**
     * @brief stop                  停止线程
     */
    void stop();

    /**
     * @brief getTask               获取任务
     * @param task                  任务方法
     * @return
     */
    bool getTask(task_t &task);

    /**
     * @brief getThreadId           获取线程ID
     * @return
     */
    inline std::string getThreadId();

private:
    bool m_isStart = false;                         // 线程池是否工作
    int m_threadCnt = 0;                            // 线程数量
    mutex m_mutex;                                  // 互斥锁
    vector<thread*> m_threadVec;                    // 线程队列
    deque<task_t> m_taskQueue;                      // 任务队列
    condition_variable  m_conditonVar;              // 条件变量,通知线程
};

#endif // MYTHREADPOLL_H
复制代码
复制代码
#include "MyThreadPoll.h"

MyThreadPoll::MyThreadPoll(int threadNum) : m_threadCnt(threadNum)
{
    start();        // 初始化线程
}

void MyThreadPoll::start()
{
    m_isStart = true;
    m_threadVec.reserve(m_threadCnt);
    for (int i=0; i<m_threadCnt; i++)
    {
        m_threadVec.push_back(std::move(new std::thread(&MyThreadPoll::threadLoop, this)));
    }
}

MyThreadPoll::~MyThreadPoll()
{
    if ( m_isStart )
    {
        stop();
    }
}

void MyThreadPoll::addTask(const task_t &task)
{
    printf("[ MyThreadPoll::%s ]\n", __FUNCTION__);
    lock_guard<mutex> m_lockguard(m_mutex);
    m_taskQueue.push_back(task);
    m_conditonVar.notify_one();     // 通知线程开始执行任务
}

void MyThreadPoll::threadLoop()
{
    printf("Thread id [ %s ] start.\n", getThreadId().c_str());

    while (m_isStart)
    {
        // 有任务, 并且任务函数已经初始化时, 执行任务函数
        task_t task;
        if ( getTask(task) )
        {
            task();
        }
    }

    printf("Thread id [ %s ] exit.\n", getThreadId().c_str());
}

bool MyThreadPoll::getTask(task_t &task)
{
    unique_lock<mutex> m_uniquelock(m_mutex);

    // 避免虚假唤醒, 先判断共享资源的有效性, 不满足条件则继续阻塞等待
    while ( m_isStart && m_taskQueue.empty() )
    {
        m_conditonVar.wait(m_uniquelock);
    }

    printf("Thread id [ %s ] wake up.\n", getThreadId().c_str());

    // 取出第一个任务
    if ( !m_taskQueue.empty() && m_isStart )
    {
        task = m_taskQueue.front();
        m_taskQueue.pop_front();
    }

    return task.operator bool();                // 任务是否有效
}

string MyThreadPoll::getThreadId()
{
    std::stringstream tmp;
    tmp << std::this_thread::get_id();
    return tmp.str();
}

void MyThreadPoll::stop()
{
    printf("MyThreadPoll::%s .\n", __FUNCTION__);
    {
        // 互斥锁, {}作用域结束后锁进行释放
        unique_lock<mutex> m_uniquelock(m_mutex);
        m_isStart = false;
        m_conditonVar.notify_all();

        printf("Notify all.\n");
    }

    // 删除全部线程
    auto iter = m_threadVec.begin();
    while( iter != m_threadVec.end() )
    {
        (*iter)->join();
        iter = m_threadVec.erase(iter);
    }
    m_threadVec.clear();
}
复制代码
复制代码
#include "MyThreadPoll.h"


int printId(int num)
{
    printf("%s | num: %d\n", __FUNCTION__, num);
    return 0;
}

int printStr(const string &str)
{
    printf("%s | str: %s\n", __func__, str.c_str());

    return 0;
}


int main(void)
{
    MyThreadPoll mythreadPool(3);    
    auto future = mythreadPool.addTask(printId, 3);
    int res = future.get();
    printf("res: %d\n", res);

    auto future2 = mythreadPool.addTask(printStr, "12345678");
    res = future2.get();
    printf("res2: %d\n", res);

    auto future3 = mythreadPool.addTask(printStr, "88888888");
    res = future3.get();
    printf("res3: %d\n", res);    
    return 0;            
}
复制代码

执行结果:

参考: 基于C++11实现线程池的工作原理 - 靑い空゛ - 博客园 (cnblogs.com)

参考:C++ 线程池 - 敬方的个人博客 | JF Blog (wangpengcheng.github.io)

posted @   蔡头一枚  阅读(60)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
历史上的今天:
2022-12-28 Linux 格式打包命令
点击右上角即可分享
微信分享提示