一、问题点:
在实际开发过程中,我们会时常和线程打交道,而且c++11及之后为我们提供了线程等相关的库。当我们需要完成某些任务但是又不想主线程被阻塞,那么创建一个线程是我们的首选。那么问题来了,既然我们能根据需求创建多个线程,那么为啥扯到线程池呢?
二、什么是线程池:
单从字面意思看,线程池不就是存放线程的容器(池)。这是一种通俗的理解。维基百科上的解释是:在计算机编程中,线程池是用于实现计算机程序中的执行的并发性的软件设计模式。通常也称为复制工作者或工作人员模型,线程池维护多个线程,等待由监督程序分配任务以便并发执行。下面是线程池的原来示意图:
线程池有两个核心概念,一个是任务队列:负责存放主线程需要处理的任务;一个是工作线程队列:负责从任务队列中取出和运行任务,工作线程队列是一个死循环。线程池相当于一个生产者和消费者的模型。
三、使用线程池的好处:
线程在创建和销毁时需要时间,线程过多会带来调度开销,进而影响缓存局部性和整体性能。线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。 例如,线程数一般取cpu数量+2比较合适,线程数过多会导致额外的线程切换开销。
四、如何实现线程池:
其实Windows早就为我们使用线程池提供了大量的API,如果在Windows系统开发且对Windows API熟悉的同学,使用Windows API就足够了,只需要掌握其使用方式就可以了。但是对于c++开发,c++标准没有为我们提供线程池的库,但是c++11提供了线程库以及一系列同步和互斥组件,使得我们实现一个线程池容易很多。下面的线程池实现代码是直接使用其他大佬写的(在此声明),贴出来方便今后查看。代码如下:
1 class ThreadPool 2 { 3 public: 4 explicit ThreadPool(size_t threadNum) 5 { 6 for (auto i = 0; i < threadNum; ++i) 7 { 8 _works.emplace_back([this]() 9 { 10 for (;;) 11 { 12 std::function<void()> task; 13 { 14 std::unique_lock<std::mutex> ul(_mtx); 15 _cv.wait(ul, [this]() { return _stop || !_tasks.empty(); }); 16 if (_stop && _tasks.empty()) 17 { 18 return; 19 } 20 task = std::move(_tasks.front()); 21 _tasks.pop(); 22 } 23 task(); 24 } 25 }); 26 } 27 } 28 29 ~ThreadPool() 30 { 31 { 32 std::unique_lock<std::mutex> ul(_mtx); 33 _stop = true; 34 } 35 36 _cv.notify_all(); 37 38 for (auto& work : _works) 39 { 40 work.join(); 41 } 42 43 } 44 45 template <typename F, typename... Args> 46 auto submit(F&& f, Args&&... args)->std::future<decltype(f(args...))> 47 { 48 auto taskPtr = std::make_shared<std::packaged_task<decltype(f(args...))()>>( 49 std::bind(std::forward<F>(f), std::forward<Args>(args)...) 50 ); 51 52 { 53 std::unique_lock<std::mutex> ul(_mtx); 54 if (_stop) 55 { 56 throw std::runtime_error("submit on stopped ThreadPool."); 57 } 58 _tasks.emplace([taskPtr]() { (*taskPtr)(); }); 59 } 60 61 _cv.notify_all(); 62 return taskPtr->get_future(); //线程return的结果 63 } 64 private: 65 bool _stop = false; 66 std::vector<std::thread> _works; //工作线程队列 67 std::queue<std::function<void()>> _tasks; //任务队列 68 std::mutex _mtx; 69 std::condition_variable _cv; 70 };
参考资料:
1、https://zh.wikipedia.org/wiki/线程池;
2、知乎《线程池的c++实现》