基于C++11的线程池(threadpool),简洁且可以带任意多的参数

您所在的位置:网站首页 线程池任务队列模式 基于C++11的线程池(threadpool),简洁且可以带任意多的参数

基于C++11的线程池(threadpool),简洁且可以带任意多的参数

2024-07-14 12:46| 来源: 网络整理| 查看: 265

咳咳。C++11 加入了线程库,从此告别了标准库不支持并发的历史。然而 c++ 对于多线程的支持还是比较低级,稍微高级一点的用法都需要自己去实现,譬如线程池、信号量等。线程池(thread pool)这个东西,在面试上多次被问到,一般的回答都是:“管理一个任务队列,一个线程队列,然后每次取一个任务分配给一个线程去做,循环往复。” 貌似没有问题吧。但是写起程序来的时候就出问题了。

废话不多说,先上实现,然后再啰嗦。(dont talk, show me ur code !)

代码实现

 文末有GitHub 链接, 但是没人去看更新的, 现在更新下好了--- 2022/06/02

1 #pragma once 2 #ifndef THREAD_POOL_H 3 #define THREAD_POOL_H 4 5 #include 6 #include 7 #include 8 #include 9 //#include 10 //#include 11 //#include 12 #include 13 14 namespace std 15 { 16 //线程池最大容量,应尽量设小一点 17 #define THREADPOOL_MAX_NUM 16 18 //线程池是否可以自动增长(如果需要,且不超过 THREADPOOL_MAX_NUM) 19 //#define THREADPOOL_AUTO_GROW 20 21 //线程池,可以提交变参函数或拉姆达表达式的匿名函数执行,可以获取执行返回值 22 //不直接支持类成员函数, 支持类静态成员函数或全局函数,Opteron()函数等 23 class threadpool 24 { 25 unsigned short _initSize; //初始化线程数量 26 using Task = function; //定义类型 27 vector _pool; //线程池 28 queue _tasks; //任务队列 29 mutex _lock; //任务队列同步锁 30 #ifdef THREADPOOL_AUTO_GROW 31 mutex _lockGrow; //线程池增长同步锁 32 #endif // !THREADPOOL_AUTO_GROW 33 condition_variable _task_cv; //条件阻塞 34 atomic _run{ true }; //线程池是否执行 35 atomic _idlThrNum{ 0 }; //空闲线程数量 36 37 public: 38 inline threadpool(unsigned short size = 4) { _initSize = size; addThread(size); } 39 inline ~threadpool() 40 { 41 _run=false; 42 _task_cv.notify_all(); // 唤醒所有线程执行 43 for (thread& thread : _pool) { 44 //thread.detach(); // 让线程“自生自灭” 45 if (thread.joinable()) 46 thread.join(); // 等待任务结束, 前提:线程一定会执行完 47 } 48 } 49 50 public: 51 // 提交一个任务 52 // 调用.get()获取返回值会等待任务执行完,获取返回值 53 // 有两种方法可以实现调用类成员, 54 // 一种是使用 bind: .commit(std::bind(&Dog::sayHello, &dog)); 55 // 一种是用 mem_fn: .commit(std::mem_fn(&Dog::sayHello), this) 56 template 57 auto commit(F&& f, Args&&... args) -> future 58 { 59 if (!_run) // stoped ?? 60 throw runtime_error("commit on ThreadPool is stopped."); 61 62 using RetType = decltype(f(args...)); // typename std::result_of::type, 函数 f 的返回值类型 63 auto task = make_shared( 64 bind(forward(f), forward(args)...) 65 ); // 把函数入口及参数,打包(绑定) 66 future future = task->get_future(); 67 { // 添加任务到队列 68 lock_guard lock{ _lock };//对当前块的语句加锁 lock_guard 是 mutex 的 stack 封装类,构造的时候 lock(),析构的时候 unlock() 69 _tasks.emplace([task]() { // push(Task{...}) 放到队列后面 70 (*task)(); 71 }); 72 } 73 #ifdef THREADPOOL_AUTO_GROW 74 if (_idlThrNum < 1 && _pool.size() 0; --size) 99 { //增加线程数量,但不超过 预定义数量 THREADPOOL_MAX_NUM 100 _pool.emplace_back( [this]{ //工作线程函数 101 while (true) //防止 _run==false 时立即结束,此时任务队列可能不为空 102 { 103 Task task; // 获取一个待执行的 task 104 { 105 // unique_lock 相比 lock_guard 的好处是:可以随时 unlock() 和 lock() 106 unique_lock lock{ _lock }; 107 _task_cv.wait(lock, [this] { // wait 直到有 task, 或需要停止 108 return !_run || !_tasks.empty(); 109 }); 110 if (!_run && _tasks.empty()) 111 return; 112 _idlThrNum--; 113 task = move(_tasks.front()); // 按先进先出从队列取一个 task 114 _tasks.pop(); 115 } 116 task();//执行任务 117 #ifndef THREADPOOL_AUTO_GROW 118 if (_idlThrNum>0 && _pool.size() > _initSize) //支持自动释放空闲线程,避免峰值过后大量空闲线程 119 return; 120 #endif // !THREADPOOL_AUTO_GROW 121 { 122 unique_lock lock{ _lock }; 123 _idlThrNum++; 124 } 125 } 126 }); 127 { 128 unique_lock lock{ _lock }; 129 _idlThrNum++; 130 } 131 } 132 } 133 }; 134 135 } 136 137 #endif //https://github.com/lzpong/

 

代码不多吧,上百行代码就完成了 线程池, 并且, 看看 commit,  哈,  不是固定参数的, 无参数数量限制!  这得益于可变参数模板.

怎么使用?

 看下面代码(展开查看)

1 #include "threadpool.h" 2 #include 3 4 void fun1(int slp) 5 { 6 printf(" hello, fun1 ! %d\n" ,std::this_thread::get_id()); 7 if (slp>0) { 8 printf(" ======= fun1 sleep %d ========= %d\n",slp, std::this_thread::get_id()); 9 std::this_thread::sleep_for(std::chrono::milliseconds(slp)); 10 } 11 } 12 13 struct gfun { 14 int operator()(int n) { 15 printf("%d hello, gfun ! %d\n" ,n, std::this_thread::get_id() ); 16 return 42; 17 } 18 }; 19 20 class A { 21 public: 22 static int Afun(int n = 0) { //函数必须是 static 的才能直接使用线程池 23 std::cout


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3