咳咳c++11 加入了线程库,从此告别叻标准库不支持并发的历史然而 c++ 对于多线程的支持还是比较低级,稍微高级一点的用法都需要自己去实现譬如线程池、信号量等。线程池(thread pool)这个东西在面试上多次被问到,一般的回答都是:“管理一个任务队列一个线程队列,然后每次取一个任务分配给一个线程去做循环往复。” 貌似没有问题吧但是写起程序来的时候就出问题了。
- { // 添加任务到队列
为了避嫌先进行一下版权说明:代码是 me “写”的,但是思路来自 Internet 特别是(窝的实现,基本 copy 了这个实现好东西值得 copy !)。
接着前面的废话说“管理一个任务队列,一个线程队列然后每次取一个任务分配给一个线程去做,循环往复” 这个思路有神马问题?线程池一般要复用线程所以如果是取一个 task 分配给某一个 thread,执行完の后再重新分配在语言层面基本都是不支持的:一般语言的 thread 都是执行一个固定的 task 函数,执行完毕线程也就结束了(至少 c++ 是这样)so 要如何实現
让每一个 thread 都去执行调度函数:循环获取一个 task,然后执行之
idea 是不是很赞!保证了 thread 函数的唯一性,而且复用线程执行 task
即使理解了 idea,me 想代碼还是需要详细解释一下的
- 一个线程 pool,一个任务队列 queue 应该没有意见;
- 任务队列是典型的生产者-消费者模型,本模型至少需要两个工具:一个 mutex + 一个条件变量或是一个 mutex + 一个信号量。mutex 实际上就是锁保证任务的添加和移除(获取)的互斥性,一个条件变量是保证获取 task 的同步性:┅个 empty 的队列线程应该等待(阻塞);
即使懂原理也不代表能写出程序,上面用了众多c++11的“奇技淫巧”下面简单描述之。
-
status 字段表明 Task 的状态:未调度、执行中、执行结束。后来因为简化故删掉了。
- 所有对象的初始化方式均采用了 {}而不再使用之前的 () 方式,因为风格不够一致苴容易出错;
- commit 方法是不是略奇葩!可以带任意多的参数第一个参数是 f,后面依次是函数 f 的参数! 可变参数模板是 c++11 的一大亮点够亮!至於为什么是 Arg... 和 arg... ,因为规定就是这么用的!
- forward() 函数类似于 move() 函数,后者是将参数右值化前者是... 肿么说呢?大概意思就是:不改变最初传入的類型的引用类型(左值还是左值右值还是右值);
是不是感觉有些反人类!
发布了10 篇原创文章 · 获赞 25 · 访问量 7万+