std::stop_token是c++20协作式取消机制的核心,用于线程主动检查是否被请求停止,解决detach后失控和bool标志内存可见性问题;需通过std::jthread传入,只能作为函数参数(不可复制或外部获取),配合stop_requested()轮询或wait()阻塞等待。

std::stop_token 是什么,它能解决什么问题
它不是线程的“杀毒软件”,不能强行中断正在执行的代码;它是协作式取消机制的核心——线程自己定期检查 stop_token 是否被请求停止,再决定是否退出。这避免了 std::thread::detach() 后失控、或用 bool 标志位因内存可见性导致漏检等老问题。
关键前提是:你得在线程函数里主动轮询,且轮询点要合理(比如循环体开头、阻塞调用前)。
怎么创建并传递 stop_token 给线程
必须通过 std::jthread(C++20 引入),它自带 std::stop_source,构造时自动绑定,比手动配 std::thread + std::stop_source 简洁安全得多。
常见错误是试图把 std::stop_token 从 std::jthread 外部“取出来”再传——不行,std::jthread 的 token 只在构造时注入,且不可复制(只可移动或隐式转换)。
- 正确方式:直接把
std::stop_token作为参数传入线程函数(编译器会自动绑定) - 错误方式:
auto tok = my_jthread.get_stop_token();——std::jthread没有get_stop_token()成员 - 若需手动控制,改用
std::stop_source+std::thread,但要自行管理生命周期,容易悬垂
std::jthread t([](std::stop_token stoken) {
while (!stoken.stop_requested()) {
do_work();
std::this_thread::sleep_for(100ms);
}
});
stop_requested() 和 wait() 该选哪个
stop_requested() 是轻量检查,适合高频轮询;wait() 是阻塞等待,适合线程大部分时间空闲、只在收到取消请求时才需响应的场景(比如监听信号、等待队列任务)。
但注意:wait() 不可重入,多次调用未匹配的 notify() 会阻塞;且一旦 stop_source 被销毁(如 std::jthread 析构),wait() 会立即返回,不报错也不抛异常——这点容易误判为“没生效”。
- 循环处理任务:优先用
stop_requested() - 长期休眠等待事件:用
wait()+ 条件变量组合更稳妥 - 不要在
wait()后再调一次stop_requested()判断——wait()返回即代表已请求停止
为什么线程没立刻停,或者 stop_requested() 总是 false
最常见原因:线程函数里根本没检查 stop_token,或检查位置太靠后(比如只在函数末尾查一次);其次是 std::jthread 对象提前析构,导致其内部 stop_source 自动调用 request_stop(),但此时线程可能还没开始运行或已退出。
另一个隐蔽坑:lambda 捕获了局部 std::stop_token 变量,而该变量生命周期短于线程——std::jthread 的参数传递机制天然规避了这点,所以坚持用参数传入,别捕获。
- 确保每次循环迭代都调用
stoken.stop_requested() - 避免在构造
std::jthread后立刻离开作用域,除非你明确希望它自动 request_stop - 调试时可在 lambda 内加日志:
std::cout
协作式取消的“优雅”不在速度,而在确定性:只要线程还在运行且检查了 token,就一定能响应。真正难的是设计好检查点的位置和频率——这取决于你的工作负载特征,而不是 API 本身。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











