std::stop_callback是c++20中响应std::stop_source取消请求的单次执行清理机制,仅在request_stop()被调用且自身未析构时自动执行一次回调,用于线程安全的轻量资源释放(如close fd、unlock mutex),不触发取消也不阻塞执行。

std::stop_callback 是什么,它在取消流程中起什么作用
std::stop_callback 是 C++20 引入的用于响应 std::stop_source 取消请求的机制。它本身不触发取消,也不管理线程生命周期,只在 std::stop_source 被调用 request_stop() 后、且该 callback 尚未被销毁时,**自动执行一次**注册的清理函数。
关键点:它不是“取消钩子”,而是“取消通知的同步执行点”——适合做资源释放(如 close fd、delete handle、unlock mutex),但不适合做耗时操作或再次调用 request_stop()。
- 必须绑定到有效的
std::stop_token,否则构造失败(抛std::system_errorwithstd::errc::operation_not_permitted) - callback 对象析构时会自动注销,无需手动管理
- 同一 token 可注册多个
std::stop_callback,执行顺序是后注册先执行(栈语义)
如何正确构造并使用 std::stop_callback
最常见错误是把 std::stop_callback 声明为局部变量却没确保它存活到取消发生 —— 它一出作用域就析构,注销回调,导致清理函数 never 被调用。
典型安全写法是把它作为类成员或 lambda 捕获到异步任务里(前提是该 lambda 生命周期覆盖整个任务):
void async_work(std::stop_token stoken) {
// ✅ 正确:stop_callback 在函数作用域内,但绑定到 stoken,
// 且其析构发生在函数返回前 → 能捕获取消
std::stop_callback cb{stoken, []{
std::cout
- 不要用
std::stop_callback*new 出来又忘了 delete —— 析构时机失控 - 不要在 callback 内部再调用
stoken.stop_requested()判断 —— 此时 stop 已确定发生,没必要 - 避免在 callback 中抛异常:C++ 标准规定若 callback 抛异常且未被捕获,会调用
std::terminate()
与 std::jthread 配合时的常见陷阱
std::jthread 自带 std::stop_source,其 get_stop_token() 返回的 token 在线程函数返回后失效。如果异步任务提前退出(比如 return 或 throw),std::stop_callback 的析构可能早于取消请求,导致清理遗漏。
正确模式是:让 std::stop_callback 的生命周期严格跟随需要清理的资源,而不是依赖线程函数作用域:
struct ResourceManager {
int fd_;
std::stop_callback cleanup_;
ResourceManager(int fd, std::stop_token stoken)
: fd_(fd), cleanup_{stoken, [fd]{ ::close(fd); }} {}
};
void worker(std::stop_token stoken) {
auto res = std::make_unique<resourcemanager>(open("/tmp/file", O_RDWR), stoken);
// res 一直存活到 worker 返回,cleanup_ 自动注册+注销
while (!stoken.stop_requested()) {
// ...
}
}</resourcemanager>
- 不要把
std::stop_callback放在临时对象里(如std::stop_callback{token, f}();),它立即析构 -
std::jthread析构时会自动调用request_stop(),但仅当线程 still joinable;如果已 detach 或已 join,则无 effect - 多个
std::stop_callback注册到同一 token 时,注意它们之间的依赖关系 —— 后注册的先执行,可能依赖先执行的释放结果
std::stop_callback 不适用于哪些场景
它不是万能取消钩子。以下情况不应依赖它:
- 需要异步通知(比如发消息给另一个线程)→ 应用
std::condition_variable+ token 配合轮询 - 清理逻辑需等待 I/O 完成(如 flush 并 sync 文件)→
std::stop_callback必须快速返回,否则阻塞取消传播 - 资源生命周期独立于当前 token(例如全局缓存句柄)→ 应改用 RAII 类型封装,或注册到更长生命周期的
std::stop_source - 想在取消后继续做日志或上报 → 这些应放在任务函数末尾,而非 callback 里(因为 callback 执行时线程可能正被中断)
真正容易被忽略的是:callback 执行时,任务线程可能仍在运行(只是收到了取消信号),所以所有访问的共享状态必须有明确同步(如 mutex、atomic),不能假设“cancel = thread is dying”。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











