std::stop_callback仅在请求停止时触发一次回调函数,本身不终止任务;任务必须主动轮询token.stop_requested()或响应回调设置的共享状态才能真正退出。

std::stop_callback 不能自动终止任务,它只负责通知
很多人以为注册了 std::stop_callback 就等于“挂了钩子”,一调 request_stop() 任务就停。其实它连线程都不碰,只是在停止请求发出时,**触发一次你写的函数体**。如果这个函数体里没做任何事,或者做的事和当前任务无关(比如只打日志、不改状态),那任务照常跑。
常见错误现象:
- 回调里设了
cancelled = true,但任务主循环压根不检查cancelled - 回调里调了
close(socket),但任务还在用那个 socket 发数据,导致未定义行为 - 回调是局部变量,线程函数返回后就析构了,后续
request_stop()完全没反应
真正起作用的前提只有一个:任务代码必须主动轮询 token.stop_requested() 或响应你通过回调设置的共享状态。否则,std::stop_callback 就是个哑巴广播站。
什么时候该用 std::stop_callback,而不是直接轮询 stop_token
直接在任务函数里写 while (!token.stop_requested()) { ... } 是最直白、最可控的做法,90% 的场景够用。而 std::stop_callback 的价值只在特定边界:
- 需要在**非工作线程**中响应停止信号,比如主线程或 IO 线程里关文件句柄、发监控日志
- 要清理**跨线程共享资源**,例如全局连接池、静态缓存、信号量
- 回调逻辑和任务主体完全解耦,比如协程框架中由
stop_callback触发父协程恢复,而非在子任务里轮询
注意:std::stop_callback 构造时捕获的 std::stop_token 必须有效;若从 lambda 捕获,别用引用捕获已析构对象——常见于把 std::jthread 和 std::stop_callback 都声明为局部变量,且顺序写反。
std::stop_callback 生命周期必须覆盖整个停止过程
它的析构会自动注销回调,所以生命周期短于停止请求发生时间,就会失效。典型误配:
<pre class="brush:php;toolbar:false;">std::jthread t([](std::stop_token token) {
std::stop_callback cb(token, []{ /* cleanup */ }); // ❌ cb 是栈对象,函数退出即销毁
while (!token.stop_requested()) { /* work */ }
});
t.request_stop(); // 此时 cb 已不存在,cleanup 不会执行
正确做法有三种:
- 把
std::stop_callback 声明在线程作用域外,比如类成员或全局/静态变量 - 用
std::unique_ptr<:stop_callback>动态管理,确保它活到线程结束 - 更推荐:直接不用它,在任务函数内轮询 + 清理,逻辑集中、无生命周期焦虑
尤其在协程中,应把它作为 Awaiter 成员,随 co_await 表达式生命周期自然管理,避免手动析构时机错位。
避免在 std::stop_callback 里做耗时或阻塞操作
回调执行不等待、不重入、不排队,一旦卡住,整个停止流程就被拖住。比如:
- 在回调里调
std::mutex::lock(),而锁正被任务线程持有 → 死锁 - 发起同步网络请求或磁盘写入 → 停止延迟不可控,甚至超时
- 调
std::condition_variable::wait()→ 它不感知stop_token,永远等下去
安全做法是:回调只做轻量、无锁、无阻塞操作,例如:
- 原子地设置标志位(
flag.store(true, std::memory_order_relaxed)) - 调用
close()关闭已知 fd(POSIX 保证异步信号安全) - 向线程安全队列 push 一条“清理任务”,交由专用线程处理
真正的清理成本,应该转移到协作式退出路径中去承担,而不是堆在回调里。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











