商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > C++ std::stop_token用法 _ C++20优雅停止线程执行【详解】

C++ std::stop_token用法 _ C++20优雅停止线程执行【详解】

  发布于2026-07-18 阅读(0)

扫一扫,手机访问

大家常犯的一个错误是,以为 std::stop_token 能直接停止线程执行。实际上,它本身并不能停止线程,只是一个观察者,真正触发停止的是 std::jthread 析构或 std::stop_source::request_stop(),而线程函数必须主动检查 stop_requested() 并退出。

C++ std::stop_token用法 _ C++20优雅停止线程执行【详解】

std::stop_token 本身不能停止线程,它只是个“观察者”;真正能触发停止的是 std::jthread 析构或 std::stop_source::request_stop(),而线程函数必须主动检查并退出。

std::jthread + stop_token 参数是唯一推荐的入门姿势

初学者很容易掉进一个坑里:手写 std::threadstd::stop_source,结果遗漏绑定或生命周期管理出错。说到底,最安全的起点就是直接用 std::jthread——它构造时自动关联一个 std::stop_source,析构时自动调用 request_stop()join()

这里有几个关键点需要记住:

  • 线程函数的第一个参数必须是 std::stop_token(或 const std::stop_token&,这样 std::jthread 才会把内部 token 自动传入
  • 如果函数签名没有这个参数,哪怕你调用了 t.request_stop(),线程也完全收不到任何信号
  • lambda 写法最直观:std::jthread t([](std::stop_token st) { while (!st.stop_requested()) { /* work */ } });
  • 不要在 lambda 里再调 t.get_stop_token() —— 那不是同一个对象,而且可能已经失效

轮询 stop_requested() 的位置比次数更重要

说到轮询,很多人的第一反应是写一个 while (!token.stop_requested()) 作为万能循环条件,结果线程卡死在系统调用里无法响应。正确做法是:在自然等待点检查,而不是无休止地忙等。

那具体该在哪些地方检查呢?

  • std::this_thread::sleep_for() 后检查
  • queue.pop() 返回前检查
  • 在一次网络读取(如 recv())完成后检查
  • 避免纯 CPU 循环:如果必须轮询,加 std::this_thread::yield() 或短延时(如 1ms

需要注意的是,那些不支持 stop_token 的阻塞调用(比如 std::mutex::lock()std::this_thread::sleep_for())根本不会被中断——它们完全不知道有停止请求的存在。

std::condition_variable::wait 等重载才是真正的“可取消等待”

标准库中真正原生支持 std::stop_token 的函数并不多,主要是 std::condition_variable::wait()wait_for()wait_until() 的重载版本,以及 std::shared_mutex 的带超时尝试锁。

来看一个例子:

std::condition_variable cv;
std::mutex mtx;
std::unique_lock lk(mtx);
cv.wait(lk, stoken, []{ return ready; }); // 收到 stop 请求时立即返回 false

这类函数在收到 request_stop() 后会直接退出阻塞状态,并返回 false(或抛出 std::stop_exception),让你有机会做清理工作。这比手动轮询要高效得多,响应也更及时。

std::stop_callback 不是线程安全的清理入口

坦白说,std::stop_callback 看起来很诱人,但它的执行时机和捕获变量的生命周期非常容易出错:

  • 回调在 std::stop_source 销毁时(比如 std::jthread 析构)被调用,也可能在 request_stop() 后立刻触发
  • 如果你在回调里捕获了局部变量(比如 [&buf]{ buf.clear(); }),而该变量已经在主线程作用域结束,那就成了悬垂引用
  • 回调函数本身不保证运行在线程上下文中——它可能在任意线程调用(取决于谁触发 request_stop()
  • 更稳妥的做法是:回调只设一个 std::atomic 标志,主循环里定期检查这个标志并做清理

真正容易被忽略的一点是:一旦 std::stop_source 被销毁,所有关联的 std::stop_tokenstop_requested() 会永远返回 true——这个状态不可逆,也不能重置。

本文转载于:https://www.php.cn/faq/2344550.html 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注