std::stop_token是C++20协作式中断机制的只读句柄,用于查询停止请求或注册回调;它不主动终止线程,需用户代码显式响应,常与std::jthread配合使用,后者自动关联std::stop_source并提供get_stop_token()。

std::stop_token 是 C++20 引入的协作式线程中断机制的核心组件,它本身**不主动终止线程**,而是一个轻量级句柄,用于查询是否已请求停止(stop_requested())或注册回调(register_callback)。真正的中断逻辑必须由用户代码显式检查并响应。
std::stop_token 怎么和 std::jthread 配合使用?
std::jthread 是 C++20 对 std::thread 的增强,构造时自动关联一个 std::stop_source,其 get_stop_token() 方法返回对应的 std::stop_token。这是最常见、最安全的获取方式。
- 每个
std::jthread拥有独立的std::stop_source,互不干扰 - 调用
jthread.request_stop()会触发所有已注册的回调,并使后续token.stop_requested()返回true - 线程函数内应定期检查
token.stop_requested(),或在阻塞点(如std::this_thread::sleep_for)后立即检查
void worker(std::stop_token token) {
while (!token.stop_requested()) {
// 做工作...
std::this_thread::sleep_for(100ms);
}
// 清理资源
}
int main() {
std::jthread t{worker};
std::this_thread::sleep_for(500ms);
t.request_stop(); // 安全触发停止
}
std::stop_token.register_callback 为什么可能不执行?
回调函数仅在「停止请求已发出」或「后续发出停止请求」时被调用,且只执行一次。常见失效原因:
- 回调对象(如 lambda)生命周期结束前未注册成功:必须确保
std::stop_callback对象(或持有它的变量)在停止发生前持续有效 - 注册时停止已请求(
token.stop_requested() == true),则回调会**立即同步执行**,若此时栈已展开或资源已释放,可能引发未定义行为 - 回调抛出异常:C++20 规定,若回调抛出异常,程序直接调用
std::terminate() - 多个回调注册顺序不确定,不保证执行顺序
std::stop_token 和 std::stop_source、std::stop_callback 的关系是什么?
三者构成完整协作中断链:
立即学习“C++免费学习笔记(深入)”;
-
std::stop_source:唯一能发起停止请求的对象,通过request_stop()触发 -
std::stop_token:只读视图,可多次拷贝,用于查询或注册回调;拷贝开销极小(通常只是指针) -
std::stop_callback:模板类,封装回调函数对象;构造时绑定stop_token并注册,析构时自动注销
注意:std::stop_token 无法反向获取 std::stop_source,这是有意设计——避免误传或滥用停止控制权。
std::stop_token 在非 jthread 场景下怎么手动管理?
可以独立创建 std::stop_source,再派生 std::stop_token,适用于自定义线程池、异步任务等场景:
std::stop_source source;
std::stop_token token = source.get_token();
// 注册回调(注意生命周期!)
auto cb = std::stop_callback{token, []{ std::cout << "stopped!\n"; }};
// 稍后触发
source.request_stop(); // 此时 cb 被调用
但务必注意:如果 source 先销毁,而 token 仍被使用(例如传入其他线程),token.stop_requested() 行为是未定义的。因此,推荐将 stop_source 与受控生命周期的对象(如 task 类)绑定,而非裸全局变量或临时对象。
最容易被忽略的一点:协作式中断不是“杀线程”,而是“通知线程该停了”——所有检查点、所有资源清理路径、所有阻塞调用后的恢复点,都得你亲手写 if (token.stop_requested()) return; 或类似逻辑。没有银弹,只有责任移交。










