简介
线程池:一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。 例如,线程数一般取cpu数量+2比较合适,线程数过多会导致额外的线程切换开销。
线程池的组成
- 线程池: 管理所有线程;
- 工作线程: 线程池中的线程,从任务队列中获取任务并执行任务;
- 任务队列: 存放需要执行的任务,本文采用deque队列可以存放优先任务;
- 添加任务接口: 用于向任务队列中添加任务;
工作原理
首先创建一定数量的线程并处于空闲状态,通过添加任务接口向任务队列添加任务并通知线程去执行,线程收到通知后从任务队列取出任务并执行,当前任务执行完后若任务队列中还有任务则继续取出并执行,否则就进入空闲等待状态。当线程池收到停止信号时等待正在执行的线程执行完成并放弃其余任务。
上代码前先简单介绍deque容器:
deque: 两端支持高效插入和删除容器的元素,是一种具有队列和栈的性质的数据结构,尽管双端队列看起来似乎比栈和队列更灵活,但实际上在应用程序中远不及栈和队列有用,如图:
代码
threadPool.h
#pragma once
#include <iostream>
#include <vector>
#include <mutex>
#include <deque>
#include <atomic>
using namespace std;
using Task = function<void()>;
class threadPool
{
public:
threadPool(int nThreadNum = 3);
~threadPool();
private:
vector<thread> m_vThreads;//线程池
deque<Task> m_Tasks;//任务队列,采用deque双端队列
condition_variable m_condVar;//条件变量
mutex m_mutex;
atomic<bool> m_bStop;//线程池结束标志
protected:
//工作线程
void workThread();
public:
//添加任务
template<class T, class... Args> void postTask(T t, Args... args)
{
if (!m_bStop)
{
//std::move,std::forward 将数据挪用于自身,避免临时变量创建和销毁带来的性能消耗
auto task = std::bind(std::forward<T>(t), std::forward<Args>(args)...);
{
unique_lock<mutex> lck(m_mutex);
m_Tasks.push_front([task]() {task(); });
}
m_condVar.notify_one();
}
}
//添加高优先级任务
template<class T, class... Args> void sendTask(T t, Args... args)
{
if (!m_bStop)
{
auto task = std::bind(std::forward<T>(t), std::forward<Args>(args)...);
{
unique_lock<mutex> lck(m_mutex);
m_Tasks.push_back([task]() {task(); });
}
m_condVar.notify_one();
}
}
//停止
void stop();
};
threadPool.cpp
#include "threadPool.h"
threadPool::threadPool(int nThreadNum):m_bStop(false)
{
int num = nThreadNum <= 0 ? 1 : nThreadNum;
for (int i=0;i<num;i++)
m_vThreads.push_back(thread(&threadPool::workThread,this));
}
threadPool::~threadPool()
{
stop();
}
void threadPool::stop()
{
if (!m_bStop)
{
m_bStop = true;
m_condVar.notify_all();//唤醒所有线程
for (auto it = m_vThreads.begin(); it != m_vThreads.end(); it++)
{
if (it->joinable())
it->join();
}
m_vThreads.clear();
}
}
void threadPool::workThread()
{
cout << "thread: " << this_thread::get_id() <<" start"<< endl;
while (!m_bStop)
{
Task task;
{
unique_lock<mutex> lck(m_mutex);
m_condVar.wait(lck, [this]() {return m_bStop.load() || !m_Tasks.empty(); });
if (m_bStop)
break;
else
{
task = move(m_Tasks.back());//获取第一个任务
m_Tasks.pop_back();//删除任务
}
}
cout << "thread: " << this_thread::get_id() << " work "<<endl;
task();//执行任务
}
cout << "thread: " << this_thread::get_id() << " end" << endl;
}