我们通过创建 std::thread 对象来对线程进行管理。在一些情况下,这种方式不可行,因为需要在线程的整个生命周期中对其进行管理,并根据硬件来确定线程数量,等等。另一种情况是,当使用多线程来解决某个问题时,在某个条件达成的时候,可以提前结束。
所以从管理线程和任务的机制,两个角度介绍线程池。
一、简单的线程池
定义:管理一个任务队列,一个线程队列,然后每次取一个任务分配给一个线程去做,循环往复。作为最简单的线程池,其拥有固定数量的工作线程(通常工作线程数量std::thread::hardware_concurrency() 相同)。当工作需要完成时,可以调用函数将任务挂在任务队列中。每个工作线程都会从任务队列上获取任务,然后执行这个任务,执行完成后再回来获取新的任务。
简单的线程池:
class thread_pool { std::atomic_bool done; thread_safe_queue<std::function<void()> > work_queue; // 1 线程安全队列管理任务队列 std::vector<std::thread> threads; // 2 工作线程 join_threads joiner; // 3 来汇聚所有线程 void worker_thread() { while(!done) // 4 { std::function<void()> task; if(work_queue.try_pop(task)) // 5 从队列上获取任务 { task(); // 6 执行任务,可在此加上线程数目增加语句。完成任务,增加一个空闲线程数目 } else { std::this_thread::yield(); // 7 让线程休息 } } } public: thread_pool(): //列表初始化类 done(false),joiner(threads) { unsigned const thread_count=std::thread::hardware_concurrency(); // 8 获取硬件支持并发数 try { for(unsigned i=0;i<thread_count;++i) { threads.push_back( std::thread(&thread_pool::worker_thread,this)); // 9 线程执行于此,每个线程都会执行获取任务的函数。但是,本简单线程池不支持线程的回收循环利用 } //可以,在回收线程那里加上--thread_count;从而可以多次循环 } catch(...) { done=true; // 10 throw; } } ~thread_pool() { done=true; // 11 } template<typename FunctionType> void submit(FunctionType f) { work_queue.push(std::function<void()>(f)); // 12 将函数f包装成一个std::function<void()>实例,推入队列之中 } };
CPU密集型的程序要(cpu个数个+1)个线程就行了,I/O密集型,因为I/O操作不需要访问CPU,所以可以创建多个线程(2*NUM_cpu).
另一个思路,先创建一定量的线程,当忙碌线程达到总线程的80%时,创建新的线程。
总之线程池通常适合下面的几个场合:
(1) 单位时间内处理任务频繁而且任务处理时间短
(2) 对实时性要求较高。如果接受到任务后在创建线程,可能满足不了实时要求,因此必须采用线程池进行预创建。
(3) 必须经常面对高突发性事件,比如Web服务器,如果有足球转播,则服务器将产生巨大的冲击。此时如果采取传统方法,则必须不停的大量产生线程,销毁线程。此时采用动态线程池可以避免这种情况的发生。
以后填坑@@//TODO