std::jthread构造时自动绑定stop_token,线程函数直接接收std::stop_token参数即可;析构时自动request_stop()并join(),但需线程函数主动轮询stop_requested()以响应取消。

std::jthread 构造时自动绑定 stop_token,无需手动传递
std::jthread 在构造时会自动生成一个 std::stop_source,并把对应的 std::stop_token 传给线程函数——这是它比 std::thread 更“优雅”的起点。你不用显式创建 std::stop_source,也不用担心忘记把 token 传进去。
常见错误是仍像用 std::thread 那样手动管理 token,结果重复构造或丢失绑定:
- 误写
std::jthread t{[](std::stop_token){});:虽然能编译,但丢掉了自动绑定的语义,token 实际来自 jthread 内部,不是你传的那个 - 试图在构造后调用
t.get_stop_source().request_stop()前,先用t.get_stop_token()去轮询——可行,但没必要,因为线程函数已天然持有有效 token
正确做法就是直接让线程函数接受 std::stop_token 参数:
std::jthread t{[](std::stop_token st) {
while (!st.stop_requested()) {
// 做工作...
std::this_thread::sleep_for(100ms);
}
// 自动调用 request_stop() 并 join()
}};
在循环中用 stop_token::stop_requested() 检查取消请求
std::stop_token::stop_requested() 是轻量、无锁、线程安全的检查方式,适合高频轮询。它不阻塞,也不抛异常,只返回 bool,符合协作式取消“主动让出”的设计哲学。
立即学习“C++免费学习笔记(深入)”;
注意几个关键点:
- 不要用
st.stop_possible()判断是否能取消——它只表示 token 是否关联了 source,几乎总是 true,对逻辑无实质帮助 - 避免在 long-running 系统调用(如
read(),accept())中只靠stop_requested()轮询,会造成 busy-wait;应配合可中断的等待机制(见下一条) - 如果线程正在等待某个条件变量,需在
wait或wait_for中传入 token,否则可能错过取消信号
例如,用 std::condition_variable::wait_until + std::stop_token 实现可中断等待:
std::jthread t{[](std::stop_token st) {
std::mutex m;
std::condition_variable cv;
bool ready = false;
std::unique_lock lk{m};
while (!ready && !st.stop_requested()) {
if (cv.wait_until(lk, std::chrono::steady_clock::now() + 1s, [&]{ return ready; })) {
break;
}
}
}};
std::jthread 析构时自动 request_stop() + join(),但需确保线程函数响应 token
这是 std::jthread 最省心的特性:离开作用域时,它会自动调用内部 stop_source.request_stop(),然后阻塞等待线程退出。但前提是——你的线程函数必须定期检查 stop_token,否则它会永远卡在某处,导致析构 hang 住。
典型陷阱:
- 线程函数里没检查 token,或者只在循环开头检查一次,之后就进入不可中断的长耗时操作(如大数组排序、文件读取)
- 使用了不支持 cancellation 的第三方库调用(如某些 C 风格网络 API),且未设超时或封装中断逻辑
- 在持有锁时长时间等待,而锁未被设计为可被中断
解决思路不是“避免析构”,而是把取消点拆得足够细。例如,在处理大量数据时插入检查:
std::jthread t{[](std::stop_token st) {
for (int i = 0; i < 1000000; ++i) {
process_item(i);
if ((i % 1000 == 0) && st.stop_requested()) {
break;
}
}
}};
需要外部触发取消?用 get_stop_source() 获取可写句柄
当线程启动后,你想从其他地方(比如主线程、信号处理器、UI 事件)主动请求停止,就得拿到它的 std::stop_source。jthread 提供 get_stop_source() 成员函数,返回一个可拷贝、可移动的句柄。
注意事项:
-
get_stop_source()返回的是副本,调用request_stop()是线程安全的,但多次调用无副作用(只会生效第一次) - 不能跨线程保存
std::stop_token后再拿去调用request_stop()——token 只读,只有 source 才能发起请求 - 如果你需要多个线程监听同一取消信号,应共享同一个
std::stop_source,而非多个 jthread 各自独立
示例:主线程在特定条件下提前终止工作线程:
std::jthread worker{[](std::stop_token st) {
while (!st.stop_requested()) {
do_work();
std::this_thread::sleep_for(50ms);
}
}};
// ... 一段时间后
if (some_condition) {
worker.get_stop_source().request_stop(); // 安全,非阻塞
}
协作式取消真正难的不是 API 调用,而是在线程执行路径的关键节点插入恰当的检查点——尤其是那些看似“短暂”实则可能因数据规模或系统负载而变长的操作。token 本身不会中断任何东西,它只提供一个“该停了”的信号;谁来响应、何时响应、响应后如何清理,全在你自己手里。







