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