std::stop_token 是 C++20 引入的协作式取消机制,本质区别于强制终止:它不发信号,仅反映 std::stop_source 是否被请求停止;线程需主动轮询 stop_requested() 或注册 stop_callback 实现安全退出与自动清理。

std::stop_token 是什么,它和传统线程终止方式有何本质区别
它不是“发信号”,而是提供一种协作式取消机制:线程主动轮询 stop_token 的状态,决定是否退出。这避免了 std::thread::interrupt()(已移除)或强制 pthread_cancel 带来的资源泄漏、析构未执行等危险。
关键点在于「协作」——stop_token 本身不触发任何操作,只反映 std::stop_source 是否被请求停止。线程必须自己检查 stop_token.stop_requested() 或注册回调。
常见误用是把它当“kill 按钮”:直接调用 stop_source.request_stop() 后就认为线程立刻结束,却没在线程函数里做轮询或没处理好临界区。
如何在线程函数中正确轮询 stop_token
最常用、最可控的方式是在循环中定期检查 stop_token.stop_requested(),尤其适用于长时间运行的计算或 I/O 等待场景。
立即学习“C++免费学习笔记(深入)”;
- 轮询位置要合理:不能只在循环开头检查,否则一次耗时操作(如
sleep_for、read())可能阻塞数秒才响应;应在每次迭代关键节点后检查 - 避免过度轮询:高频调用
stop_requested()几乎无开销,但若在 tight loop 中每毫秒查一次,属于无谓消耗,应结合业务节奏(如每 10ms 或每次处理完一个任务后查) - 注意原子性:
stop_requested()是无锁、无内存序副作用的轻量调用,无需额外同步
示例:
void worker(std::stop_token stoken) {
while (!stoken.stop_requested()) {
do_work();
std::this_thread::sleep_for(10ms); // 长等待前/后都建议检查
if (stoken.stop_requested()) break;
handle_next_task();
}
cleanup(); // 保证执行
}
如何用 stop_callback 实现“退出时自动清理”
当线程逻辑复杂、存在多处退出路径(如异常、提前返回、多个循环出口),手动在每个分支加 cleanup() 易遗漏。此时 std::stop_callback 提供 RAII 式保障:只要 stop_source.request_stop() 被调用,绑定的回调就会在该 token 关联的线程上下文中**同步执行一次**(即使线程已退出,回调仍会执行)。
- 回调对象生命周期必须长于
stop_callback实例,通常捕获局部变量需注意:推荐用值捕获或确保引用对象不早于线程结束 - 回调内不要调用阻塞操作(如
std::mutex::lock()),因为它是同步触发的,可能卡住request_stop()调用方 - 一个
stop_token可绑定多个stop_callback,触发顺序是后注册先执行(栈语义)
示例:
void worker(std::stop_token stoken) {
std::vector<int> buffer(1024);
std::stop_callback cb{stoken, [&buffer]{
std::cout << "Cleaning up buffer of size " << buffer.size() << "\n";
buffer.clear(); // 安全:buffer 在作用域内
}};
while (!stoken.stop_requested()) {
fill_buffer(buffer);
if (buffer.empty()) break;
}
}
request_stop() 调用时机与线程安全边界
std::stop_source::request_stop() 是线程安全的,可从任意线程调用,但它的效果取决于目标线程是否正在监听该 token —— 它不会中断系统调用,也不会暂停 CPU 执行,只是把内部标志置为 true。
- 调用后不等于线程已终止:必须等待线程自行检测并退出,必要时需
join()或detach()前确认状态 - 重复调用无害:
request_stop()幂等,多次调用只生效一次 - 注意 token 有效性:若
stop_token来自已销毁的stop_source(如局部stop_source出作用域),其stop_requested()永远返回 false,且stop_callback不会触发 —— 这是最隐蔽的 bug 来源之一
典型错误写法:
std::thread t{[stoken = std::stop_source{}.get_token()] {
// ❌ stoken 关联的 stop_source 已析构,stop_requested() 永远为 false
}};
真正需要关注的,是 stop_source 的生存期管理 —— 它得活到所有依赖它的线程完成清理为止。多数情况下,把它作为类成员或静态对象更稳妥。


















