线程池分类:
- 半同步半异步线程池
- 领导者追随者线程池——实现见 linux服务器开发 2019.2.7(libevent实现http server,线程池)
线程池原理:
- 预先创建一定数量的线程,当任务请求到来时从线程池中分配一个预先创建的线程的处理任务,线程在处理完任务之后还可以重用,不会被销毁。
- 避免了大量的线程创建和销毁动作,从而节省了系统资源
构架:
排队层分析:
上图中线程池分为三层结构,其中,排队层处于核心地位,因为上侧会将任务加到排队层中,异步服务层同时会去除任务,这里有一个同步的过程。
实现时,排队层就是一个同步队列,允许多个线程同时去添加或者取出任务,并且要保证操作过程是安全的。
线程池有两个活动过程,一个是往同步队列中添加任务,一个是从同步队列中取出任务:
同步队列实现代码——SyncQueue.hpp
#include<iostream>
#include<list>
#include<thread>
#include<condition_variable>
#include<mutex>
using namespace std;
template<typename T>
class SyncQueue{
public:
SyncQueue(int maxSize):m_maxSize(maxSize), m_needStop(false){}
void Put(const T&x){
Add(x);
}
void Put(T&&x){
Add(forward<T>(x));
}
void Take(list<T>& list){
//获取互斥mutex
unique_lock<mutex> locker(m_mutex);
//如果需要停止或者队列不为空,则释放mutex并将线程置于waiting状态
//wait状态等待其他线程调用notify_one或者notify_all将其唤醒
m_notEmpty.wait(locker, [this]{return m_needStop || NotEmpty();});
if(m_needStop) return;
//取出任务队列中的任务
list=move(m_queue);
//唤醒一个处于等待状态的用于添加任务的线程去添加任务
m_notFull.notify_one();
}
void Take(T& t){
unique_lock<mutex>locker(m_mutex);
m_notEmpty.wait(locker, [this]{return m_needStop || NotEmpty();});
if(m_needStop) return;
t=m_queue.front();
m_queue.pop_front();
m_notFull.notify_one();
}
void Stop(){
{
lock_guard<mutex>locker(m_mutex);
//m_needStop放在了lock_guard的保护范围之内
m_needStop=true;
}
//放在lock_guard保护范围之外是为了优化
//notify_all或notify_one会唤醒一个等待的线程,线程被唤醒之后会先获取mutex
//如果在lock_guard保护内,则唤醒的线程需要lock_guard析构释放mutex才能获取mutex
//放在lock_guard保护范围之外则无需等待lock_guard释放锁
m_notFull.notify_all();
m_notEmpty.notify_all();
}
bool Empty(){
lock_guard<mutex>locker(m_mutex);
return m_queue.empty();
}
bool Full(){
lock_guard<mutex>locker(m_mutex);
return m_queue.size()==m_maxSize;
}
size_t Size(){
lock_guard<mutex>locker(m_mutex);
return m_queue.size();
}
int Count(){
return m_queue.size();
}
private:
bool NotFull() const{
bool full=m_queue.size()>=m_maxSize;
if(full) cout<<"缓冲区满了,需要等待。。。"<<endl;
return !full;
}
bool NotEmpty() const{
bool empty=m_queue.empty();
if(empty)
cout<<"缓冲区空了,需要等待...异步层线程ID:"
<<this_thread::get_id()<<endl;
return !empty;
}
template<typename F>
void Add(F&&x){
//获取mutex
unique_lock<mutex>locker(m_mutex);
m_notFull.wait(locker, [this]{return m_needStop || NotFull();});
if(m_needStop) return;
//队列不满
m_queue.push_back(forward<F>(x));
m_notEmpty.notify_one();
}
private:
list<T>m_queue; //缓冲区
mutex m_mutex; //互斥量和条件变量结合使用
condition_variable m_notEmpty; //不为空的条件变量
condition_variable m_notFull; //没有满的条件变量
int m_maxSize; //同步队列最大的size
bool m_needStop; //停止标志
};
线程池的实现——ThreadPool.hpp
#include<thread>
#include<list>
#include<functional>
#include<memory>
#include<atomic>
#include"SyncQueue.hpp"
const int MaxTaskCount=100;
class ThreadPool{
public:
using Task=function<void()>;
//创建线程数为当前cpu核数
ThreadPool(int numThreads=thread::hardware_concurrency()):m_queue(numThreads){}
~ThreadPool(void){
//如果没有停止时,则主动停止线程池
Stop();
}
void Stop(){
//保证多线程情况下只调用一次StopThreadGroup
call_once(m_flag, [this]{StopThreadGroup();});
}
void AddTask(Task&& task){
m_queue.Put(forward<Task>(task));
}
void AddTask(const Task&& task){
m_queue.Put(task);
}
void Start(int numThreads){
m_running=true;
cout<<"创建异步线程的数量:"<<numThreads<<endl;
//创建线程组
for(int i=0; i<numThreads; i++){
m_threadgroup.push_back(make_shared<thread>(&ThreadPool::RunInThread, this));
}
}
private:
void RunInThread(){
while(m_running){
//取任务分别执行
list<Task>list;
m_queue.Take(list);
for(auto& task : list){
if(!m_running) return;
task();
}
}
}
void StopThreadGroup(){
m_queue.Stop();
m_running=false;
for(auto& thread : m_threadgroup){
if(thread) thread->join();
}
m_threadgroup.clear();
}
list<shared_ptr<thread>>m_threadgroup; //处理任务的线程组
SyncQueue<Task>m_queue; //同步队列
atomic_bool m_running; //是否停止的标志
once_flag m_flag;
};
使用示例——main.cpp
#include<iostream>
#include<list>
#include<functional>
#include<memory>
#include<atomic>
#include<thread>
#include<chrono>
#include"ThreadPool.hpp"
void TestThdPool(){
ThreadPool pool;
pool.Start(2);
thread thd1([&pool]{
for(int i=0; i<10; i++){
auto thdId=this_thread::get_id();
pool.AddTask([thdId]{
cout<<"同步层线程 1 的线程 ID:"<<thdId<<endl;
});
}
});
thread thd2([&pool]{
for(int i=0; i<10; i++){
auto thdId=this_thread::get_id();
pool.AddTask([thdId]{
cout<<"同步层线程2的线程ID:"<<thdId<<endl;
});
}
});
this_thread::sleep_for(chrono::seconds(2));
getchar();
pool.Stop();
thd1.join();
thd2.join();
}
int main(){
TestThdPool();
return 0;
}
g++编译
g++ -std=c++11 -pthread -lpthread main.cpp -o ThreadPool
运行结果
线程池创建了两个内部的线程,线程ID分别为:和。初始时,线程池的同步队列是空的,因此两个线程进入等待状态,知道队列中有数据才开始处理数据。
线程池的上层有两个线程,线程ID分别为:和。这两个线程不断的网同步队列中添加数据,这些数据供异步服务层的线程处理。最终结果是,异步层的线程交替处理来自上层的任务,交替打印出上层的线程ID,缓冲区空了就会等待,满了之后也会等待,不会允许无限制的添加任务。