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以前,是不包含线程库的,开发多线程,必须使用OS系统自带的线程API,这就导致了很多问题,最主要的是,跨平台的开发,一般要保持几个主流应用的库的代码支持,特别是对…...

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

【合合TextIn】AI构建新质生产力,合合信息Embedding模型助力专业知识应用
目录 一、合合信息acge模型获MTEB中文榜单第一 二、MTEB与C-MTEB 三、Embedding模型的意义 四、合合信息acge模型 (一)acge模型特点 (二)acge模型功能 (三)acge模型优势 五、公司介绍 一、合合信息…...

Flutter 拦截系统键盘,显示自定义键盘
一、这里记录下在开发过程中,下单的时候输入金额需要使用自定义的数字键盘 参考链接: https://juejin.cn/post/7166046328609308685 效果图 二、屏蔽系统键盘 怎样才能够在输入框获取焦点的时候,不让系统键盘弹出呢?同时又显示我们自定义的…...
内存泄漏是什么?如何避免内存泄漏?
1.2 内存泄漏 使用new开辟空间泄漏,抛出异常 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系统中,syslog是一种系统日志服务,用于收集、存储和管理系统和应用程序生成的日志消息。syslog服务负责记录系统的运行状态、错误信息、警告、调试信息等,以便系统管理员可以监控系统的健康状况、故障排查和性能优化。 含义和作用&am…...

kubernetes(K8S)学习(一):K8S集群搭建(1 master 2 worker)
K8S集群搭建(1 master 2 worker) 一、环境资源准备1.1、版本统一1.2、k8s环境系统要求1.3、准备三台Centos7虚拟机 二、集群搭建2.1、更新yum,并安装依赖包2.2、安装Docker2.3、设置hostname,修改hosts文件2.4、设置k8s的系统要求…...
巧克力(蓝桥杯)
文章目录 巧克力题目描述解题分析贪心 巧克力 题目描述 小蓝很喜欢吃巧克力,他每天都要吃一块巧克力。 一天小蓝到超市想买一些巧克力。超市的货架上有很多种巧克力,每种巧克力有自己的价格、数量和剩余的保质期天数,小蓝只吃没过保质期的…...
Python爬虫之pyquery和parsel的使用
三、pyquery的使用 1、准备工作 pip3 install pyquery2、初始化 2.1、字符串初始化 把HTML的内容当做参数,来初始化PyQuery对象。 html <div><ul><li class"item-0">first item</li><li class"item-1">&l…...

移动硬盘怎么加密?移动硬盘加密软件有哪些?
移动硬盘是我们在工作中最常用的移动存储设备,为了保护数据安全,需要使用专业的移动硬盘加密软件加密保护。那么,移动硬盘加密软件有哪些? BitLocker BitLocker是Windows的磁盘加锁功能,可以用于加密保护移动硬盘中…...
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. 相交链表 - 力扣(LeetCode) 给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。 图示两个链表在节…...
C#WPF控件大全
本文列出WPF控件大全,点击可以进入详情页查看。 列表如下: AccessText用下划线来指定用作访问键的字符。 ActivatingKeyTipEventArgs为 ActivatingKeyTip 事件提供数据。...

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

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

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

【pytest、playwright】多账号同时操作
目录 方案实现思路: 方案一: 方案二: 方案实现思路: 依照上图所见,就知道,一个账号是pytest-playwright默认的环境,一个是 账号登录的环境 方案一: 直接上代码: imp…...

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

【C++】stack、queue和优先级队列
一、前言 二、stack类 2.1 了解stack 2.2 使用stack (1)empty (2)size (3)top (4)push (5)pop 2.3 stack的模拟实现 三、queue类 3.1 了解queue …...

【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...

P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...

人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

群晖NAS如何在虚拟机创建飞牛NAS
套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...
Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成
一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...