std::stop_token是C++20引入的协作式线程取消机制核心,用于安全、可等待地响应停止请求,解决裸bool标志内存可见性差、阻塞调用无法及时中断、多线程竞态等问题。

std::stop_token 是什么,它能解决什么问题
std::stop_token 是 C++20 引入的协作式线程取消机制的核心组件,用来替代裸 bool 标志或信号量等手工轮询方式。它本身不强制终止线程,而是提供一种安全、可等待、可传递的“停止请求通知”能力——适用于需要响应中断、清理资源、避免死锁的长期运行线程(比如网络监听、定时任务、后台计算)。
常见错误现象包括:
- 用
volatile bool running+while(running),但无法保证内存可见性,或在阻塞调用(如std::this_thread::sleep_for、queue.pop())中无法及时响应 - 手动
pthread_cancel或std::thread::join前未同步状态,导致析构时访问已释放资源 - 多个子线程共用一个停止标志但无原子性保障,出现竞态
std::stop_token 的关键价值在于:它绑定到 std::stop_source,而后者可被任意线程调用 request_stop();所有持有对应 stop_token 的线程都能通过 stop_requested() 或 stop_callback 感知变化,并在合适时机退出。
如何在线程函数中正确轮询和响应 stop_token
轮询不是“每毫秒查一次”,而是嵌入自然暂停点,避免空转耗 CPU。典型模式是把 stop_token 传入线程函数,并在循环中检查:
立即学习“C++免费学习笔记(深入)”;
- 使用
token.stop_requested()判断是否收到请求(轻量、无副作用) - 在阻塞调用前加检查,例如:
if (token.stop_requested()) break; - 若使用支持停顿的等待函数(如
std::condition_variable::wait_until),优先搭配std::stop_token重载版本(C++20 提供)
void worker(std::stop_token token) {
while (!token.stop_requested()) {
// 做一部分工作
do_work_step();
<pre class='brush:php;toolbar:false;'> // 关键:在可能长时间阻塞前检查
if (token.stop_requested()) break;
// 推荐:用带 token 的 wait(自动处理唤醒与停止)
std::unique_lock lk(mtx);
cv.wait(lk, token, [&]{ return !queue.empty(); });
if (!queue.empty()) {
auto task = std::move(queue.front());
queue.pop();
task();
}
}
cleanup_resources(); // 确保此处执行}
注意:不要在构造函数或 RAII 对象中隐式依赖 stop_token 生命周期;它只在绑定的 std::stop_source 有效期内合法。
std::stop_callback 能帮你避开哪些陷阱
std::stop_callback 是注册在 stop_token 上的回调对象,一旦 stop_source.request_stop() 被调用,它会在该 token 所属线程上下文中立即执行(前提是线程仍在运行且 token 未失效)。这解决了两个常见痛点:
- 避免轮询遗漏:即使线程正卡在系统调用里(如
read()、accept()),只要回调注册了,就能触发清理逻辑 - 解耦“请求停止”和“执行清理”:你可以在启动线程时一次性注册资源释放逻辑,后续无需在每个循环分支里重复写
if (stop) cleanup()
void worker(std::stop_token token) {
FileHandle file = open("log.txt", O_WRONLY);
<pre class='brush:php;toolbar:false;'>// 注册自动清理 —— 只要 request_stop() 被调用,就会关闭文件
std::stop_callback cb(token, [&file]{
if (file.valid()) file.close();
});
while (!token.stop_requested()) {
write_log(file);
std::this_thread::sleep_for(1s);
}}
容易踩的坑:
-
std::stop_callback构造时捕获的变量必须在线程生命周期内有效(不能捕获局部栈变量地址) - 回调函数不能抛异常(否则程序 terminate)
- 同一个
stop_token可注册多个回调,但它们执行顺序未指定
request_stop() 调用时机和线程安全边界
std::stop_source::request_stop() 是唯一触发停止的入口,但它不是线程安全的“广播”操作:它只会使所有关联的 stop_token 的 stop_requested() 返回 true,并唤醒等待中的 wait 调用。它的线程安全性仅限于:可从任意线程多次调用,重复调用无副作用。
关键约束:
-
request_stop()必须在std::stop_source对象仍存活时调用(不能在std::stop_source已析构后调用) - 不要期望它能中断正在执行的非协作代码(比如一个死循环中没检查 token 的 long-running compute)
- 如果线程已退出,其
stop_token自动变为无效(stop_requested()永远返回 false),但注册的stop_callback已被自动销毁
典型安全用法:
- 把
std::stop_source存在主线程或管理类中,由外部控制生命周期 - 在
std::threadjoin 前调用request_stop(),再join() - 若线程因异常退出,确保
stop_source不被提前销毁(例如用std::shared_ptr<:stop_source></:stop_source>共享所有权)
最常被忽略的一点:std::stop_token 和 std::stop_source 是 move-only 类型,不能拷贝;跨线程传递时必须显式 move,否则编译失败或静默截断。


















