std::stop_token 是用于线程协作式取消的监听机制,让工作线程主动感知停止请求并安全退出;它不强制终止线程,而是配合 std::jthread 实现自动请求停止、支持回调注册与标准库等待函数集成,避免竞态和资源泄漏。

std::stop_token 是什么,它能解决什么问题
它不是用来“杀线程”的,而是让工作线程主动感知“该停了”,从而自行退出循环、清理资源、返回。没有它,你可能得靠 volatile bool + sleep 或 condition_variable 等方式轮询,既浪费 CPU,又容易漏掉停止信号或产生竞态。
std::stop_token 配合 std::jthread(C++20 引入)使用最自然:jthread 构造时自动关联一个 std::stop_source,析构时自动请求停止;而 stop_token 就是那个“监听器”。
关键点:
-
std::jthread的析构会调用request_stop(),但仅当线程还在运行且未被 request 过 -
std::stop_token本身不可变,不能“触发停止”,只能stop_possible()和stop_requested() - 多个线程可共享同一个
std::stop_source,但每个线程应持有自己的std::stop_token
如何在线程函数里正确轮询 stop_token
别写成 while (!token.stop_requested()) { /<em> work </em>/ } 就完事——这在无休眠的密集计算中没问题,但一旦涉及等待(比如 queue.pop()、socket.recv()),必须配合可中断等待。
推荐模式:
- 对支持
std::stop_token的标准等待函数(如std::condition_variable::wait的重载),直接传入 token,它会在 stop_requested 时唤醒并返回false - 对不支持的阻塞调用(如
read()、recv()、第三方库的 wait),需用std::this_thread::sleep_for(1ms)等短间隔轮询 + 检查 token,或改用非阻塞 I/O +select/epoll等机制 - 永远在循环开头检查
token.stop_requested(),避免执行完一轮冗长操作后才响应停止
void worker(std::stop_token token) {
while (!token.stop_requested()) {
auto item = queue.try_pop(); // 非阻塞
if (item.has_value()) {
process(*item);
} else {
std::this_thread::sleep_for(10ms); // 避免空转
}
}
cleanup(); // 这里做释放、flush 等
}
为什么不能只靠 jthread 析构自动停止
std::jthread 析构时确实会调用 request_stop(),但它不会等线程真正退出——它只发请求,然后 detach 或 join(取决于是否已 joinable)。如果你在 jthread 对象销毁后立刻访问被它使用的资源(比如 shared_ptr 指向的缓冲区),就可能踩内存。
典型陷阱:
- 把
std::jthread声明为局部变量,函数返回前就析构 → 请求发出,但线程可能还在跑,访问已释放的queue或logger - 没显式
join()或request_stop()后等待,就认为“线程已安全结束” - 多个 jthread 共享一个
std::shared_ptr<t></t>,但 T 的析构依赖 stop_token 通知,结果 T 被提前销毁
正确的做法是:明确控制生命周期,例如:
auto th = std::jthread(worker, token); // ... 使用中 th.request_stop(); // 显式请求 th.join(); // 确保退出后再释放资源
stop_token 与旧式 cancel flag 对比的实际差异
旧方法(volatile bool / atomicstd::stop_token 把“取消权”和“监听权”分离得更干净。
具体区别:
-
std::stop_source是唯一能调用request_stop()的对象,天然防误触;而atomic<bool>*</bool>可被任意代码置 true -
stop_token支持register_callback(),允许注册一个函数,在 stop_requested 发生时自动调用(适合清理注册表、关闭文件句柄等) - 标准库组件(如
std::condition_variable::wait、std::future::wait_until)原生支持stop_token,无需手动封装 - stop_token 不可复制但可移动,避免意外共享;atomic
复制后就是两个独立变量,容易逻辑错乱
真正容易被忽略的是:stop_token 的 callback 注册时机必须在线程启动之后、且不能在 token 失效后调用 register_callback()(会抛 std::stop_callback_registered 异常)。这意味着 callback 应该在 worker 函数内部注册,而不是构造 jthread 前。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











