当前位置: 首页 > news >正文

C++20中的jthread

一、多线程开发

c++11以前,是不包含线程库的,开发多线程,必须使用OS系统自带的线程API,这就导致了很多问题,最主要的是,跨平台的开发,一般要保持几个主流应用的库的代码支持,特别是对Win平台和Linux平台的两个线程库的支持,否则,就无法实现简单的跨平台的功能。而两个平台的多线程的使用差异和一些细节不同的,比如事件Event在Linux上没有。而条件变量在Win平台上没有,就更容易使一些应用场景的开发的复杂性大幅提高。
而到了c++11以后,提供了std::thread这个多线程的支持,可以说大幅的降低了跨平台开发的复杂性。
但是在实际的应用过程中,又遇到了一些新的需求变化,比如应用的方便性和多线程运行过程中的可操作性及可见性上,都需要提供新的接口。按照c++设计者们的一贯作风,他们不会在基础的std::thread本身进行修改,那么,他们就对这个类进行了再封装,增加了上述的功能,然后提出了std::jthread这个类。

二、jthread的功能和实现

一般来说,接口越丰富,那么这个类功能越强大,但是反过来,缺点就是不容易掌握并且容易忘记调用,在使用std::thread时,有没有忘记调用join()或者detach()的时候?自动调用好不好?线程函数运行过程中,可不可以可以动态请求交互一下,而不是靠各种变量来预先进行控制。对,在std::jthread上都实现了。
看一看标准文档是如何定义的:

std::jthread::jthread
jthread() noexcept;                              (1)	(C++20)
jthread( jthread&& other ) noexcept;             (2)	(C++20)
template< class Function, class... Args >
explicit jthread( Function&& f, Args&&... args );(3)	(C++20)
jthread( const jthread& ) = delete;              (4)	(C++20)

第一个表示创建新的jthread对象;第二个表示移动语义,既然移动了,那么原来线程停止执行线程,由新线程执行;第三个表示线程函数f接受 std::stop_token 作为其首参数,则新线程开始执行:

std::invoke(decay_copy(std::forward<Function>(f)),get_stop_token(),decay_copy(std::forward<Args>(args))...);

;否则它开始执行

std::invoke(decay_copy(std::forward<Function>(f)),decay_copy(std::forward<Args>(args))...);

任一情况下, decay_copy 定义为

template <class T>
std::decay_t<T> decay_copy(T&& v) { return std::forward<T>(v); }

除了在调用方的语境中执行 decay_copy ,故而在求值和移动/构造参数期间抛出的任何异常都在当前线程抛出,而不会开始新线程。
构造函数的完成同步于(按 std::memory_order 中定义) f 的副本在新线程上调用的开始。
若 std::remove_cvref_t 与 std::jthread 为相同类型则此构造函数不参与重载决议。
最后一个表示不可复制,也就是说只能它自己执行线程,没有任何其它一个std::jthread对象可以表示同一线程。

std::jthread的成员函数里,可以看支持查看硬件并发数的函数,交换线程对象的函数,协作请示的函数等,更多的详情可以参看一下:
https://zh.cppreference.com/w/cpp/thread/jthread/jthread
https://zh.cppreference.com/w/cpp/header/stop_token

三、应用

线程的中断和取消是需要高度引起注意的,一不小心,强行KILL的线程中的各种资源或者句柄就没有回收,导致各种泄露。这也是在多线程编程中,普遍有一个要求,就是让线程自己执行完毕退出,而为了这个要求,就需要牺牲一下控制的效率。
当然,先从std::thread的多线程编程的例子看:

#include <Windows.h>
#include <iostream>
#include <thread>
#include <chrono>bool quit = false;void SleepTime(int sec)
{std::this_thread::sleep_for(std::chrono::seconds(sec));
}
void ThreadWorker()
{//work work workstd::cout << "thread start,working! " << std::endl;
}void MainWorker()
{std::cout << "main thread start,working! " << std::endl;
}
int main()
{std::thread t = std::thread([&]() {while (!quit){ThreadWorker();SleepTime(2);}});//Sleep(3);MainWorker();t.join();//t.detach();
}

这个DEMO非常简单,在主线程中启动了一个子线程,需要注意一下注释的延时的情况,主要是起一个先后交替的条件。主线程在执行完成自己的工作后,就会join等待子线程的完成,像上面这种情况,就死循环了,不会停止。如果需要停止只能使用设置quit为True或者强制的命令等不友好的手段了。首先看一下,换成jthread会是啥样?其它代码都没有改变,只是修改了下面的代码:

std::jthread t = std::jthread([&]() {while (!quit){ThreadWorker();SleepTime(2);}
});

需要包含的jthread等的头文件可以去github或者相关的网站上下载,这里只提供一处,并对此表示感谢:
https://github.com/josuttis/jthread/tree/master/source
上面的两处代码的运行结果是完全一样的,没有什么不同。那用这个类的优势呢?别急,把main函数改一下:

int main()
{std::jthread t = std::jthread([&]() {while (!quit){ThreadWorker();SleepTime(2);}});Sleep(3);MainWorker();//t.join();//t.detach();
}

把最后一行注释,再编译运行,仍然没有啥问题,有一点不同了吧。这可以避免粗心大意的同学出现意外错误吧。再看一下,如果设置quit为true,那么线程会怎么办?没啥大问题,到了判断就退出了,可是如果有多个线程在执行,是不是要设置多个类似的变量呢?答案是肯定的。这时候儿看看jthread的协作中断:

int main()
{std::jthread t = std::jthread([&]() {while (!quit){ThreadWorker();SleepTime(2);}});Sleep(3);MainWorker();t.request_stop();//简单增加一行代码
}

编译运行,发现没啥反应,好,再修改一下线程函数:

int main()
{std::jthread t = std::jthread([&](std::stop_token st) {while (/*!quit*/!st.stop_requested()){ThreadWorker();SleepTime(2);}});Sleep(3);MainWorker();//getchar();t.request_stop();//t.join();//t.detach();getchar();
}

这个时候儿发现没啥问题了,注意上面getchar()函数的位置,如果在后面,则执行一次就退出了。如果在前面,就继续不断执行。把t.request_stop();这一行也注释,发现程序仍然和不注释一样,看来这个jthread里做了什么运作,看一下它的析构函数:

inline jthread::~jthread() {if (joinable()) {   // if not joined/detached, signal stop and wait for end:request_stop();join();}
}

明白了吧。至于为什么不用detach(),可能是大牛们觉得join()会更安全一些吧。毕竟等待完成后,所有的对象都被回收了。下面再看官网提供的一个例子:

class foo
{
public:void bar(){for (int i = 0; i < 5; ++i) {std::cout << "Thread 3 executing\n";++n;std::this_thread::sleep_for(std::chrono::milliseconds(10));}}int n = 0;
};
int main()
{foo f;std::jthread t3(&foo::bar, &f);std::jthread t = std::jthread([&](std::stop_token st) {while (/*!quit*/!st.stop_requested()){ThreadWorker();SleepTime(2);}});std::cout << t.hardware_concurrency() << std::endl;Sleep(3);MainWorker();getchar();t.request_stop();//t.join();//t.detach();
}

执行一下,就可以看出上面提到的decay_copy这个模板函数的使用,没啥不容易理解的。

4、总结

其实还有几个特点值得说说,但不准备再讲了,虽然已经投票完成确定了c++20的标准,但毕竟还没有正式增加进去,大家有兴趣可以看看源码。整体来看,c++应用还是越来越方便,这对c++来说,肯定是个好事情。现在就希望>VS2019或者>GCC11中,尽快完全支持。

相关文章:

C++20中的jthread

一、多线程开发 c11以前&#xff0c;是不包含线程库的&#xff0c;开发多线程&#xff0c;必须使用OS系统自带的线程API&#xff0c;这就导致了很多问题&#xff0c;最主要的是&#xff0c;跨平台的开发&#xff0c;一般要保持几个主流应用的库的代码支持&#xff0c;特别是对…...

Xception模型详解

简介 Xception的名称源自于"Extreme Inception"&#xff0c;它是在Inception架构的基础上进行了扩展和改进。Inception架构是Google团队提出的一种经典的卷积神经网络架构&#xff0c;用于解决深度卷积神经网络中的计算和参数增长问题。 与Inception不同&#xff0…...

【合合TextIn】AI构建新质生产力,合合信息Embedding模型助力专业知识应用

目录 一、合合信息acge模型获MTEB中文榜单第一 二、MTEB与C-MTEB 三、Embedding模型的意义 四、合合信息acge模型 &#xff08;一&#xff09;acge模型特点 &#xff08;二&#xff09;acge模型功能 &#xff08;三&#xff09;acge模型优势 五、公司介绍 一、合合信息…...

Flutter 拦截系统键盘,显示自定义键盘

一、这里记录下在开发过程中&#xff0c;下单的时候输入金额需要使用自定义的数字键盘 参考链接: https://juejin.cn/post/7166046328609308685 效果图 二、屏蔽系统键盘 怎样才能够在输入框获取焦点的时候&#xff0c;不让系统键盘弹出呢&#xff1f;同时又显示我们自定义的…...

内存泄漏是什么?如何避免内存泄漏?

1.2 内存泄漏 使用new开辟空间泄漏&#xff0c;抛出异常 int main() {int size 0;try{while (1){//int* p (int*)malloc(sizeof(int) * 1024 * 1024);/*if (p NULL){break;}*/int* p new int[1024 * 1024];size size 4 * 1024 * 1024;cout << p << endl;}}…...

linux 中的syslog的含义和用法

在Linux系统中&#xff0c;syslog是一种系统日志服务&#xff0c;用于收集、存储和管理系统和应用程序生成的日志消息。syslog服务负责记录系统的运行状态、错误信息、警告、调试信息等&#xff0c;以便系统管理员可以监控系统的健康状况、故障排查和性能优化。 含义和作用&am…...

kubernetes(K8S)学习(一):K8S集群搭建(1 master 2 worker)

K8S集群搭建&#xff08;1 master 2 worker&#xff09; 一、环境资源准备1.1、版本统一1.2、k8s环境系统要求1.3、准备三台Centos7虚拟机 二、集群搭建2.1、更新yum&#xff0c;并安装依赖包2.2、安装Docker2.3、设置hostname&#xff0c;修改hosts文件2.4、设置k8s的系统要求…...

巧克力(蓝桥杯)

文章目录 巧克力题目描述解题分析贪心 巧克力 题目描述 小蓝很喜欢吃巧克力&#xff0c;他每天都要吃一块巧克力。 一天小蓝到超市想买一些巧克力。超市的货架上有很多种巧克力&#xff0c;每种巧克力有自己的价格、数量和剩余的保质期天数&#xff0c;小蓝只吃没过保质期的…...

Python爬虫之pyquery和parsel的使用

三、pyquery的使用 1、准备工作 pip3 install pyquery2、初始化 2.1、字符串初始化 把HTML的内容当做参数&#xff0c;来初始化PyQuery对象。 html <div><ul><li class"item-0">first item</li><li class"item-1">&l…...

移动硬盘怎么加密?移动硬盘加密软件有哪些?

移动硬盘是我们在工作中最常用的移动存储设备&#xff0c;为了保护数据安全&#xff0c;需要使用专业的移动硬盘加密软件加密保护。那么&#xff0c;移动硬盘加密软件有哪些&#xff1f; ​BitLocker BitLocker是Windows的磁盘加锁功能&#xff0c;可以用于加密保护移动硬盘中…...

openEuler 22.03 安装 .NET 8.0

openEuler 22.03 安装 .NET 8.0 openEuler 22.03 安装 .NET 8.0 openEuler 22.03 安装 .NET 8.0 查看内核信息 [jeffPC-20240314EIAA ~]$ cat /proc/version Linux version 5.15.146.1-microsoft-standard-WSL2 (root65c757a075e2) (gcc (GCC) 11.2.0, GNU ld (GNU Binutils)…...

【转载】OpenCV ECC图像对齐实现与代码演示(Python / C++源码)

发现一个有很多实践代码的git 库,特记录下: 地址:GitHub - luohenyueji/OpenCV-Practical-Exercise: OpenCV practical exercise 作者博客地址:https://blog.csdn.net/LuohenYJ 已关注。 Items项目Resources1age_gender1基于深度学习识别人脸性别和年龄Model2OpenCV_dlib_…...

每日一题(相交链表 )

欢迎大家来我们主页进行指导 LaNzikinh-CSDN博客 160. 相交链表 - 力扣&#xff08;LeetCode&#xff09; 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节…...

C#WPF控件大全

本文列出WPF控件大全,点击可以进入详情页查看。 列表如下: AccessText用下划线来指定用作访问键的字符。 ActivatingKeyTipEventArgs为 ActivatingKeyTip 事件提供数据。...

好书推荐 《AIGC重塑金融》

作者&#xff1a;林建明 来源&#xff1a;IT 阅读排行榜 本文摘编自《AIGC 重塑金融&#xff1a;AI 大模型驱动的金融变革与实践》&#xff0c;机械工业出版社出版 这是最好的时代&#xff0c;也是最坏的时代。尽管大模型技术在金融领域具有巨大的应用潜力&#xff0c;但其应…...

【Linux】权限理解

权限理解 1. shell命令以及运行原理2. Linux权限的概念3. Linux权限管理3.1 文件访问者的分类&#xff08;人&#xff09;3.2 文件类型和访问权限&#xff08;事物属性&#xff09;3.2.1 文件类型3.2.2 基本权限 3.3 文件权限值的表示方法3.4 文件访问权限的相关设置方法3.4.1 …...

插入排序、归并排序、堆排序和快速排序的稳定性分析

插入排序、归并排序、堆排序和快速排序的稳定性分析 一、插入排序的稳定性二、归并排序的稳定性三、堆排序的稳定性四、快速排序的稳定性总结 在计算机科学中&#xff0c;排序是将一组数据按照特定顺序进行排列的过程。排序算法的效率和稳定性是评价其优劣的两个重要指标。稳定…...

【pytest、playwright】多账号同时操作

目录 方案实现思路&#xff1a; 方案一&#xff1a; 方案二&#xff1a; 方案实现思路&#xff1a; 依照上图所见&#xff0c;就知道&#xff0c;一个账号是pytest-playwright默认的环境&#xff0c;一个是 账号登录的环境 方案一&#xff1a; 直接上代码&#xff1a; imp…...

软考 系统架构设计师系列知识点之云原生架构设计理论与实践(8)

接前一篇文章&#xff1a;软考 系统架构设计师系列知识点之云原生架构设计理论与实践&#xff08;7&#xff09; 所属章节&#xff1a; 第14章. 云原生架构设计理论与实践 第2节 云原生架构内涵 14.2 云原生架构内涵 关于云原生的定义有众多版本&#xff0c;对于云原生架构的…...

【C++】stack、queue和优先级队列

一、前言 二、stack类 2.1 了解stack 2.2 使用stack &#xff08;1&#xff09;empty &#xff08;2&#xff09;size &#xff08;3&#xff09;top &#xff08;4&#xff09;push &#xff08;5&#xff09;pop 2.3 stack的模拟实现 三、queue类 3.1 了解queue …...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战

前言 现在我们有个如下的需求&#xff0c;设计一个邮件发奖的小系统&#xff0c; 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其…...

Linux 文件类型,目录与路径,文件与目录管理

文件类型 后面的字符表示文件类型标志 普通文件&#xff1a;-&#xff08;纯文本文件&#xff0c;二进制文件&#xff0c;数据格式文件&#xff09; 如文本文件、图片、程序文件等。 目录文件&#xff1a;d&#xff08;directory&#xff09; 用来存放其他文件或子目录。 设备…...

智慧医疗能源事业线深度画像分析(上)

引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试

作者&#xff1a;Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位&#xff1a;中南大学地球科学与信息物理学院论文标题&#xff1a;BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接&#xff1a;https://arxiv.…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战

“&#x1f916;手搓TuyaAI语音指令 &#x1f60d;秒变表情包大师&#xff0c;让萌系Otto机器人&#x1f525;玩出智能新花样&#xff01;开整&#xff01;” &#x1f916; Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制&#xff08;TuyaAI…...

精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南

精益数据分析&#xff08;97/126&#xff09;&#xff1a;邮件营销与用户参与度的关键指标优化指南 在数字化营销时代&#xff0c;邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天&#xff0c;我们将深入解析邮件打开率、网站可用性、页面参与时…...

GC1808高性能24位立体声音频ADC芯片解析

1. 芯片概述 GC1808是一款24位立体声音频模数转换器&#xff08;ADC&#xff09;&#xff0c;支持8kHz~96kHz采样率&#xff0c;集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器&#xff0c;适用于高保真音频采集场景。 2. 核心特性 高精度&#xff1a;24位分辨率&#xff0c…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件&#xff0c;这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下&#xff0c;实现高效测试与快速迭代&#xff1f;这一命题正考验着…...

Java编程之桥接模式

定义 桥接模式&#xff08;Bridge Pattern&#xff09;属于结构型设计模式&#xff0c;它的核心意图是将抽象部分与实现部分分离&#xff0c;使它们可以独立地变化。这种模式通过组合关系来替代继承关系&#xff0c;从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...