0

0

c++20的std::stop_token和std::jthread如何优雅地中断线程? (协作式取消)

穿越時空

穿越時空

发布时间:2026-01-15 15:03:30

|

309人浏览过

|

来源于php中文网

原创

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

c++20的std::stop_token和std::jthread如何优雅地中断线程? (协作式取消)

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;应配合可中断的等待机制(见下一条)
  • 如果线程正在等待某个条件变量,需在 waitwait_for 中传入 token,否则可能错过取消信号

例如,用 std::condition_variable::wait_until + std::stop_token 实现可中断等待:

Shakespeare
Shakespeare

一款人工智能文案软件,能够创建几乎任何类型的文案。

下载
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 本身不会中断任何东西,它只提供一个“该停了”的信号;谁来响应、何时响应、响应后如何清理,全在你自己手里。

相关专题

更多
登录token无效
登录token无效

登录token无效解决方法:1、检查token的有效期限,如果token已经过期,需要重新获取一个新的token;2、检查token的签名,如果签名不正确,需要重新获取一个新的token;3、检查密钥的正确性,如果密钥不正确,需要重新获取一个新的token;4、使用HTTPS协议传输token,建议使用HTTPS协议进行传输 ;5、使用双因素认证,双因素认证可以提高账户的安全性。

6085

2023.09.14

登录token无效怎么办
登录token无效怎么办

登录token无效的解决办法有检查Token是否过期、检查Token是否正确、检查Token是否被篡改、检查Token是否与用户匹配、清除缓存或Cookie、检查网络连接和服务器状态、重新登录或请求新的Token、联系技术支持或开发人员等。本专题为大家提供token相关的文章、下载、课程内容,供大家免费下载体验。

804

2023.09.14

token怎么获取
token怎么获取

获取token值的方法:1、小程序调用“wx.login()”获取 临时登录凭证code,并回传到开发者服务器;2、开发者服务器以code换取,用户唯一标识openid和会话密钥“session_key”。想了解更详细的内容,可以阅读本专题下面的文章。

1059

2023.12.21

token什么意思
token什么意思

token是一种用于表示用户权限、记录交易信息、支付虚拟货币的数字货币。可以用来在特定的网络上进行交易,用来购买或出售特定的虚拟货币,也可以用来支付特定的服务费用。想了解更多token什么意思的相关内容可以访问本专题下面的文章。

1222

2024.03.01

登录token无效
登录token无效

登录token无效解决方法:1、检查token的有效期限,如果token已经过期,需要重新获取一个新的token;2、检查token的签名,如果签名不正确,需要重新获取一个新的token;3、检查密钥的正确性,如果密钥不正确,需要重新获取一个新的token;4、使用HTTPS协议传输token,建议使用HTTPS协议进行传输 ;5、使用双因素认证,双因素认证可以提高账户的安全性。

6085

2023.09.14

登录token无效怎么办
登录token无效怎么办

登录token无效的解决办法有检查Token是否过期、检查Token是否正确、检查Token是否被篡改、检查Token是否与用户匹配、清除缓存或Cookie、检查网络连接和服务器状态、重新登录或请求新的Token、联系技术支持或开发人员等。本专题为大家提供token相关的文章、下载、课程内容,供大家免费下载体验。

804

2023.09.14

token怎么获取
token怎么获取

获取token值的方法:1、小程序调用“wx.login()”获取 临时登录凭证code,并回传到开发者服务器;2、开发者服务器以code换取,用户唯一标识openid和会话密钥“session_key”。想了解更详细的内容,可以阅读本专题下面的文章。

1059

2023.12.21

token什么意思
token什么意思

token是一种用于表示用户权限、记录交易信息、支付虚拟货币的数字货币。可以用来在特定的网络上进行交易,用来购买或出售特定的虚拟货币,也可以用来支付特定的服务费用。想了解更多token什么意思的相关内容可以访问本专题下面的文章。

1222

2024.03.01

Golang gRPC 服务开发与Protobuf实战
Golang gRPC 服务开发与Protobuf实战

本专题系统讲解 Golang 在 gRPC 服务开发中的完整实践,涵盖 Protobuf 定义与代码生成、gRPC 服务端与客户端实现、流式 RPC(Unary/Server/Client/Bidirectional)、错误处理、拦截器、中间件以及与 HTTP/REST 的对接方案。通过实际案例,帮助学习者掌握 使用 Go 构建高性能、强类型、可扩展的 RPC 服务体系,适用于微服务与内部系统通信场景。

8

2026.01.15

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Django 教程
Django 教程

共28课时 | 3.1万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.1万人学习

Sass 教程
Sass 教程

共14课时 | 0.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号