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; }
执行结果:
越努力,越幸运
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
2022-12-28 Linux 格式打包命令