c++20中应使用std::stop_source实现生产者-消费者任务的优雅终止:一、用std::stop_token守卫循环并配合wait_until;二、封装可停止队列类内聚停止逻辑;三、跨层级传播stop_token至子任务;四、结合std::jthread自动管理生命周期。

如果您在 C++20 中实现了一个依赖多线程协作的生产者-消费者任务,且需要在运行中响应外部中断请求(如用户取消、超时或系统关闭),直接使用 volatile bool 或共享标志位可能导致竞态、遗漏唤醒或资源泄漏。std::stop_source 提供了标准化、协作式、可传播的停止机制。以下是利用 std::stop_source 实现优雅终止的具体方法:
一、基于 std::stop_token 的循环守卫与条件等待
此方法通过将 std::stop_token 传入生产者与消费者线程函数,在循环入口处检查是否已请求停止,并配合 std::condition_variable::wait_until 使用带停止令牌的重载,使等待可被及时中断。
1、在主线程中声明 std::stop_source 对象,获取其关联的 std::stop_token。
2、将该 stop_token 作为参数传递给生产者和消费者线程的启动函数或 lambda。
3、在生产者循环中,每次生成新项前调用 token.stop_requested() 判断是否应退出;若为真,则 break 循环并清理本地资源。
4、在消费者循环中,使用 cond_var.wait_until(lock, timeout_time, [&token]{ return token.stop_requested(); }) 替代无条件 wait,确保超时或停止请求任一满足即返回。
5、循环体末尾不执行 notify_one/notify_all,改为在显式退出前调用 cond_var.notify_all(),确保所有阻塞线程能感知终止信号。
二、封装可停止的生产者-消费者队列类
此方法将 std::stop_source 成员嵌入自定义队列类,使停止逻辑内聚于数据结构内部,生产者与消费者仅需调用成员函数,无需手动管理 token 传递与检查。
1、在队列类中声明 private: std::stop_source m_stop_src; 并提供 public: std::stop_token get_stop_token() const { return m_stop_src.get_token(); }。
2、修改 push() 方法:在加锁后、插入前,检查 m_stop_src.stop_requested(),若为 true 则立即返回 false 表示拒绝入队。
3、修改 pop() 方法:使用 wait_until 与 m_stop_src.get_token() 配合,当等待返回 false 或 token 被请求停止时,直接返回 std::nullopt。
4、添加 shutdown() 方法:调用 m_stop_src.request_stop(),随后调用 cond_var.notify_all() 唤醒所有等待线程。
5、在析构函数中调用 shutdown(),确保对象销毁时自动触发协作终止。
三、跨层级停止传播:父任务向子任务转发 stop_token
当生产者或消费者自身还启动了子任务(例如消费者处理某项时创建异步解析线程),需将顶层 stop_token 传递至子任务,使其也能响应同一停止请求,避免子线程滞留。
1、在创建子线程时,将当前作用域持有的 std::stop_token 作为参数传入子线程函数。
2、子线程函数签名形如 void worker(std::stop_token token),并在其内部循环或长时间操作中周期性调用 token.stop_requested()。
3、若子任务使用 std::jthread,可直接构造 jthread(token, worker_func),此时 jthread 析构时会自动 request_stop 并 join,无需手动管理。
4、对子任务中使用的 std::condition_variable,统一采用 wait_until(..., token) 形式,确保阻塞点均可响应停止。
5、关键提示:不可将 std::stop_source 复制传递给子任务——stop_source 的 request_stop() 是独占操作,应仅由最上层控制者调用;子任务只接收并监听其关联的 stop_token。
四、结合 std::jthread 简化生命周期管理
std::jthread 是 C++20 引入的可自动 join 的线程类型,其构造时可接受 std::stop_token,且析构时自动调用 request_stop 并 join,大幅降低手动同步出错概率。
1、声明 std::jthread prod_thread 和 cons_thread,构造时传入 lambda 及主线程的 stop_token。
2、lambda 函数签名应为 [](std::stop_token token) { /* 主体逻辑 */ },编译器会自动绑定。
3、在 lambda 内部,所有循环条件均以 !token.stop_requested() 为守卫,而非硬编码 true。
4、移除显式的 thread.join() 调用,jthread 析构时自动完成;若需提前终止,直接调用 prod_thread.request_stop()。
5、注意:jthread 的 request_stop() 是线程安全的,可在任意时刻从任意线程调用,但仅首次调用生效。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











