为什么需要SingleThreadPool?
-
顺序执行:保证任务按提交顺序执行(FIFO)
-
线程安全:单线程自然线程安全,不需要锁
-
简单场景:适合不需要并发的场景
-
测试调试:排查并发问题时很有用
线程池类型对比表
| 特性 | FixedThreadPool | CachedThreadPool | SingleThreadPool |
|---|---|---|---|
| 线程数 | 固定 | 动态(核心-最大) | 固定为1 |
| 创建时机 | 启动时创建 | 按需创建 | 启动时创建 |
| 销毁时机 | 永不销毁 | 空闲超时销毁 | 永不销毁 |
| 执行顺序 | 不保证 | 不保证 | 严格FIFO |
| 线程安全 | 需要锁 | 需要锁 | 自然安全 |
| 适用场景 | 稳定负载 | 突发流量 | 顺序任务 |
syncqueue.hpp
cpp
#ifndef SINGLESYNCQUEUE.HPP
#define SINGLESYNCQUEUE.HPP
#include<iostream>
#include<mutex>
#include<thread>
#include<condition_variable>
#include<vector>
#include<queue>
#include<chrono>
#include<atomic>
using namespace std;
template<typename T>
class syncqueue{
private:
queue<T>tasks;
condition_variable notempty;
mutable mutex mtx;
bool isstop;
public:
syncqueue():isstop(false){}
//禁止拷贝
syncqueue(const syncqueue&)=delete;
syncqueue& operator=(const syncqueue&)=delete;
void put(const T&task){
lock_guard<mutex>lock(mtx);
if(isstop){
throw runtime_error("队列已停止,不能添加任务");
}
tasks.push(task);
notempty.notify_one();
}
void put(T&&task){
lock_guard<mutex>lock(mtx);
if(isstop){
throw runtime_error("队列已停止,不能添加任务");
}
tasks.push(task);
notempty.notify_one();
}
bool take(T& task){//取出任务(阻塞)
unique_lock<mutex>lock(mtx);
notempty.wait(lock,[this](){return isstop||!tasks.empty();});
if(isstop&&tasks.empty()){
return false;
}
task=move(tasks.front());
tasks.pop();
return true;
}
~syncqueue(){
stop();
}
void stop(){
lock_guard<mutex>lock(mtx);
isstop=true;
notempty.notify_all();
}
bool empty()const{
lock_guard<mutex>lock(mtx);
return tasks.empty();
}
size_t getsize()const{
lock_guard<mutex>lock(mtx);
return tasks.size();
}
bool stopped()const{
lock_guard<mutex>lock(mtx);
return isstop;
}
};
#endif
singlethreadpool
cpp
#ifndef SINGLETHREADPOOL.HPP
#define SINGLETHREADPOOL.HPP
#include<iostream>
#include<mutex>
#include<thread>
#include<condition_variable>
#include<vector>
#include<queue>
#include<chrono>
#include<future>
#include<atomic>
#include<functional>
#include"singlesyncqueue.hpp"
using namespace std;
class singlethreadpool{
public:
using task=function<void()>;
private:
thread worker;
syncqueue<task>queue_;
atomic<bool>running;
once_flag stop_flag;
void workerthread(){
while(running){
task t;
if(queue_.take(t)){
try{t();}
catch(const exception&e){
cerr<<"任务执行异常"<<e.what()<<endl;
}
}
}
}
void stopworker(){
running=false;
queue_.stop();
if(worker.joinable())worker.join();
}
public:
singlethreadpool():running(true){
worker=thread(&singlethreadpool::workerthread,this);
cout<<"pool启动"<<endl;
}
~singlethreadpool(){
stop();
}
singlethreadpool(const singlethreadpool&)=delete;
singlethreadpool& operator=(const singlethreadpool&)=delete;
void stop(){
call_once(stop_flag,[this](){stopworker();cout<<"pool停止"<<endl;});
}
template<typename Func,typename...Args>
void execute(Func&&func,Args&&...args){
auto task=bind(forward<Func>(func),forward<Args>(args)...);//:绑定函数和参数,forward保持参数的左值/右值属性
queue_.put(task);
}
template<typename Func,typename...Args>
auto submit(Func&&func,Args&&...args)->future<decltype(func(args...))>{
using returntype=decltype(func(args...));
auto task=make_shared<packaged_task<returntype()>>(bind(forward<Func>(func),forward<Args>(args)...));
future<returntype>result=task->get_future();
queue_.put([task](){(*task)();});
return result;
};
size_t queuesize()const{
return queue_.getsize();
}
bool queueempty()const{
return queue_.empty();
}
bool isrunning()const{
return running;
}
thread::id getthreadid()const{
return worker.get_id();
}
};
#endif
SingleThreadPool核心特点
1. 最简单的队列
// 相比FixedThreadPool的队列:
// - 不需要notFull条件变量(因为是单线程,不会积压)
// - 只需要notEmpty条件变量
// - 不需要队列上限控制(或者可以设个很大的值)
2. 严格的FIFO
// 任务提交顺序 = 执行顺序
// 因为只有一个线程,天然保证顺序
3. 天然线程安全
// 共享数据(如counter)不需要加锁
// 因为所有任务都在同一个线程执行
4. 调试友好
// 排查bug时,可以先换成SingleThreadPool
// 如果问题消失,说明是并发问题