c++oding="utf-8" ?>
stop_token 是c++20中用于协作式线程取消的轻量级只读句柄,仅支持查询 stop_requested() 或注册回调,不提供阻塞等待或事件唤醒能力;需在循环中主动轮询或配合 register_callback 进行异步清理。

stop_token 是什么,为什么不能直接在线程函数里轮询
stop_token 本身不提供“主动监听”能力,它只是一个轻量级的只读句柄,用于查询是否已请求停止(stop_requested())或注册回调(register_callback())。它没有阻塞等待、事件唤醒或内部线程安全队列——这意味着你不能把它当作一个信号量或条件变量来“监听”。常见错误是以为 stop_token 会自动触发某段逻辑,结果发现 stop_requested() 一直返回 false,只因为没在合适时机检查。
如何在线程函数中正确轮询 stop_token
最常用且可靠的方式是在循环体中定期调用 stop_requested(),尤其适用于可拆分为多个小工作单元的场景(如处理队列、迭代大数据集、状态机步进等)。
- 必须在每次关键操作前或后检查,不能只在循环开头查一次就长期阻塞
- 如果线程在系统调用(如
std::this_thread::sleep_for、read()、wait())中阻塞,stop_token不会中断它;需改用带超时的版本并手动检查 - 避免高频无意义轮询:可用
std::this_thread::yield()或短时 sleep 缓解 CPU 占用
示例:
void worker(std::stop_token stoken) {
while (!stoken.stop_requested()) {
do_some_work();
if (stoken.stop_requested()) break; // 双重检查更稳妥
std::this_thread::sleep_for(10ms); // 避免忙等
}
cleanup();
}
如何用 register_callback 实现异步响应取消
register_callback() 允许你在其他线程调用 request_stop() 时,由实现决定何时同步执行回调(通常在 request_stop 调用方线程中,或由库内部调度)。它不是“线程内监听”,而是“事后响应”,适合资源清理类逻辑。
- 回调函数必须是无捕获 lambda 或函数指针,且不能抛异常
- 回调执行时机不确定:可能在
request_stop()当前线程中立即执行,也可能延迟;不能依赖它来中断正在运行的计算 - 若需确保回调在目标线程上下文中执行(比如释放仅该线程拥有的资源),得自己加同步机制(如
std::condition_variable+ 标志位)
示例:
void worker(std::stop_token stoken) {
std::stop_callback cb{stoken, []{ cleanup_on_stop(); }};
while (!stoken.stop_requested()) {
do_some_work();
}
// 此处仍需主动 cleanup,因为回调不一定已执行
}
常见陷阱和兼容性注意点
C++20 的 std::jthread 自动管理 stop_source,但很多旧代码还在用 std::thread + 手动 std::stop_source。这两者行为一致,但容易漏掉传递 stop_token。
- 忘记把
stop_token传入线程函数:lambda 捕获或参数列表里漏掉,导致内部始终拿到默认构造的无效 token - 在非支持平台(如旧版 libstdc++ 或 MSVC 19.28 之前)使用,
stop_token可能被静默忽略或编译失败 -
stop_requested()返回 true 后,再次调用request_stop()无效果,但多次注册 callback 是允许的(只保留最后一次注册的生效)
真正难处理的是那些无法拆解、长时间阻塞的系统调用(如 accept()、pthread_cond_wait())。这种情况下,stop_token 本身帮不上忙,得配合文件描述符事件、信号、或平台特定取消机制(如 Linux 的 eventfd + epoll)来实现响应式退出。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











