std::stop_token是c++20引入的协作式线程取消机制,用于安全通知线程主动退出;它不强制终止线程,而是通过与std::stop_source配合提供原子、单向、不可逆的停止请求,相比传统bool标志位具备线程安全和一次触发多次通知能力。

std::stop_token 是什么,它和传统线程退出有什么区别
它不是用来“杀掉”线程的,而是让线程自己感知到“该停了”,然后安全清理资源、退出循环。传统用 bool 标志位容易漏读、不保证可见性,而 std::stop_token 配合 std::stop_source 提供了原子、线程安全、一次触发多次通知的协作式取消机制。
关键点:停止请求是单向、不可逆的;stop_token 本身不拥有停止状态,只观察它;真正发出请求的是 std::stop_source。
如何在线程函数里正确监听 stop_token
最常见写法是把 std::stop_token 作为参数传入线程函数,并在循环中定期检查 stop_token.stop_requested()。注意不能只检查一次——必须在每次迭代或关键等待点检查。
- 不要在 long-running 计算中间忽略检查,否则可能卡住数秒甚至更久
- 若线程在
std::this_thread::sleep_for或std::condition_variable::wait中阻塞,优先使用带stop_token的重载(如wait(..., stop_token)),它们能自动响应停止请求并提前返回 - 如果用的是老式
wait_for,需手动结合stop_token轮询,否则无法及时退出
示例:
void worker(std::stop_token stoken) {
while (!stoken.stop_requested()) {
do_work();
std::this_thread::sleep_for(100ms);
}
cleanup(); // 确保这里执行
}
std::jthread 怎么简化 stop_token 的使用
std::jthread 是 C++20 引入的“可协作中断的线程”,构造时自动绑定 stop_source 和 stop_token,且在析构时自动调用 request_stop() —— 这意味着你不用手动管理生命周期,也不用显式传参。
- 直接把普通函数传给
jthread构造函数,它会自动注入stop_token参数(只要函数签名接受) - 函数签名可以是
void(std::stop_token)、void(const std::stop_token&),甚至void()(此时 token 被忽略) - 如果线程函数捕获了外部
stop_token,反而可能出问题:jthread 自己的 token 和你捕获的不一致,导致 request_stop() 失效
推荐写法:
std::jthread t([](std::stop_token stoken) {
while (!stoken.stop_requested()) {
// ...
}
}); // 离开作用域时自动 request_stop()
常见错误:stop_token 不生效的几个典型原因
现象是调用了 request_stop(),但线程还在跑。大概率是下面这几个地方没对上:
- 传给线程函数的
stop_token和你调用request_stop()的stop_source不是同一个对象(比如复制了stop_source,而副本的get_token()返回的是独立 token) - 线程函数参数类型写成
std::stop_token token(值传递),导致内部持有一个副本,和外部 source 断开关联;应改为const std::stop_token&或直接依赖 jthread 自动注入 - 在 lambda 捕获列表里写了
[token = source.get_token()],这又是一个独立副本,永远收不到通知 - 忘记在循环体里调用
stop_requested(),或者只在开头检查了一次
调试时可加日志:std::cout ,确认调用前后是否变化。
真正难的不是调用 request_stop(),而是确保所有路径(包括异常分支、提前 return、等待点)都尊重这个信号。哪怕一个 while 循环里漏掉一次检查,就可能让线程 hang 在那里。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











