std::stop_token 仅用于观察停止请求,真正触发停止的是 std::jthread 析构或 std::stop_source::request_stop(),且线程函数必须主动检查 stop_requested() 并退出;推荐使用 std::jthread 配 std::stop_token 参数的组合,因其自动绑定、安全析构并确保 token 正确传递。

std::stop_token 本身不能停止线程,它只是个“观察者”;真正能触发停止的是 std::jthread 析构或 std::stop_source::request_stop(),而线程函数必须主动检查并退出。
std::jthread + stop_token 参数是唯一推荐的入门姿势
别手写 std::thread 配 std::stop_source,容易漏绑定、错生命周期。用 std::jthread 是最安全的起点——它构造时自动关联一个 std::stop_source,析构时自动 request_stop() 并 join()。
关键点:
- 线程函数**第一个参数必须是
std::stop_token(或const std::stop_token&)**,std::jthread才会把内部 token 自动传入 - 如果函数签名没这个参数,哪怕你调
t.request_stop(),线程也完全收不到信号 - lambda 写法最直观:
std::jthread t([](std::stop_token st) { while (!st.stop_requested()) { /* work */ } }); - 不要在 lambda 里再调
t.get_stop_token()—— 那不是同一个对象,且可能已失效
轮询 stop_requested() 的位置比次数更重要
常见错误是把 while (!token.stop_requested()) 当成万能循环条件,结果线程卡死在系统调用里无法响应。
立即学习“C++免费学习笔记(深入)”;
正确做法是:在自然等待点检查,而不是无休止忙等:
- 在
std::this_thread::sleep_for()后检查 - 在
queue.pop()返回前检查 - 在一次网络读取(如
recv())完成后检查 - 避免纯 CPU 循环:若必须轮询,加
std::this_thread::yield()或短延时(如1ms)
不支持 stop_token 的阻塞调用(如 std::mutex::lock()、std::this_thread::sleep_for())不会被中断——它们根本不知道有停止请求。
std::condition_variable::wait 等重载才是真正的“可取消等待”
标准库中只有少数函数原生支持 std::stop_token:主要是 std::condition_variable::wait()、wait_for()、wait_until() 的重载版本,以及 std::shared_mutex 的带超时尝试锁。
例如:
std::condition_variable cv;
std::mutex mtx;
std::unique_lock<std::mutex> lk(mtx);
cv.wait(lk, stoken, []{ return ready; }); // 收到 stop 请求时立即返回 false
这类函数在收到 request_stop() 后会直接退出阻塞状态,并返回 false(或抛 std::stop_exception),让你有机会做清理。这是比手动轮询更高效、更及时的响应方式。
std::stop_callback 不是线程安全的清理入口
std::stop_callback 看起来很美,但它的执行时机和捕获变量生命周期极易出错:
- 回调在
std::stop_source销毁时(比如std::jthread析构)被调用,也可能在request_stop()后立刻触发 - 如果你在回调里捕获了局部变量(比如
[&buf]{ buf.clear(); }),而该变量已在主线程作用域结束,就是悬垂引用 - 回调函数本身不保证运行在线程上下文中——它可能在任意线程调用(取决于谁触发
request_stop()) - 更稳妥的做法是:回调只设一个
std::atomic<bool></bool>标志,主循环里定期检查这个标志并做清理
真正容易被忽略的是:一旦 std::stop_source 被销毁,所有关联的 std::stop_token 的 stop_requested() 永远返回 true——这个状态不可逆,也不能重置。


















