前言
当前罗列了以下几个线程池相关的库进行对比:
- Poco
- boost
- std::jthread
Poco 线程池
Poco::Runnable
class Runnable
{
public:
virtual void run() = 0;
};
Poco::Task
class Task: public Runnable, public RefCountedObject
{
public:
enum TaskState
{
TASK_IDLE,
TASK_STARTING,
TASK_RUNNING,
TASK_CANCELLING,
TASK_FINISHED
};
Task(const std::string& name);
const std::string& name() const;
float progress() const;
virtual void cancel();
bool isCancelled() const;
TaskState state() const;
virtual void runTask() = 0;
protected:
bool sleep(long milliseconds);
/// If the task is cancelled while it is sleeping,
/// sleep() will return immediately and the return
/// value will be true. If the time interval
/// passes without the task being cancelled, the
/// return value is false.
///
/// A Task should use this method in favor of Thread::sleep().
void setProgress(float progress);
virtual void postNotification(Notification* pNf);
/// Posts a notification to the task manager's
/// notification center.
///
/// A task can use this method to post custom
/// notifications about its progress.
};
- 支持设置任务进度
- 支持取消任务(任务状态变化)
- 支持发送通知
Notification
- 支持可中断的
sleep
。取消任务时sleep
会立即返回 - 不支持在任意位置判断是否取消
- 不支持在任意位置设置任务进度
- 不支持将取消事件传递给其它模块内部使用,其它模块内部在工作时无法立即感知取消事件的发生
Poco::Thread::current
- 支持设置线程名称
- 支持判断线程是否结束
- 支持可中断的
trySleep
。调用wakeUp
时可唤醒该函数,使该函数立即返回 - 支持线程本地存储
Poco::ThreadPool
- 支持动态线程池大小。可设置线程池动态大小、空闲多久将线程销毁(销毁时机:默认每添加32个任务触发一次销毁机制)
- 池中无空闲线程时,添加任务将抛异常
NoThreadAvailableException
- 不管理
Poco::Runnable
生命周期,任务对象结束后需要手动释放该对象
Poco::TaskManager
- 支持订阅发布模式
NotificationCenter
,外部可订阅任意感兴趣的通知Notification
- 管理了
Poco::Task
对象列表及其生命周期。任务结束时该对象将从列表中移除。当对象引用计数为 0 时销毁 - 使用
Poco::ThreadPool
做为线程池 - 池中无空闲线程时,添加任务将抛异常
NoThreadAvailableException
- 不支持任务队列
Boost 线程池
boost::this_thread
- 支持通知线程中断
关于通知线程中断
// 可通过线程对象的interrupt函数,通知该线程中断
boost::thread t(foo);
t.interrupt();
// 以下函数在调用时都可以立即感知到中断设置,并抛出异常boost::thread_interrupted
boost::thread::join()
boost::thread::timed_join()
boost::thread::try_join_for()
boost::thread::try_join_until()
boost::condition_variable::wait()
boost::condition_variable::timed_wait()
boost::condition_variable::wait_for()
boost::condition_variable::wait_until()
boost::condition_variable_any::wait()
boost::condition_variable_any::timed_wait()
boost::condition_variable_any::wait_for()
boost::condition_variable_any::wait_until()
boost::thread::sleep()
boost::this_thread::sleep_for()
boost::this_thread::sleep_until()
boost::this_thread::interruption_point()
boost::asio::thread_pool
- 不支持动态线程池大小,仅支持固定大小
- 支持任务队列。通过
boost::asio::post
函数,向队列中添加任务
std::jthread
(C++20及更新版本支持)
- std::jthread 对象析构时会自动调用 join等待其线程退出
- 支持通知线程停止
为什么不往 std::thread 添加新接口,而是引入了一个新的类?
因为 std::jthread 为了实现上述新功能,带来了额外的性能开销。
关于通知线程停止
// 为了实现通知线程停止,一共引入了2个概念
// 1. std::stop_source: 停止事件的发送者
// 2. std::stop_token: 停止事件的观察者
void thread_entry(std::stop_token stoken)
{
for (;;)
{
std::this_thread::sleep_for(300ms);
if (stoken.stop_requested())
{
return; // 观察到停止事件,立即结束线程
}
}
}
std::jthread jt(thread_entry);
std::stop_source ss;
std::stop_token st;
bool b = false;
ss = jt.get_stop_source(); // 可以将【停止事件的发送者】传参到任何线程中供调用
st = jt.get_stop_token(); // 可以在线程内部获取观察者,以观察停止事件是否被触发,也可以在线程以外观察
// 以下两行代码是等价的。get_stop_source返回std::jthread对象内部执有的发送者
b = ss.request_stop();
b = jt.request_stop(); // 其内部就是调用的上一行代码
// 观察是否执行了停止请求。该接口用于在线程中执行,判断是否需要结束当前线程
b = st.stop_requested();