[C++并发编程] 线程基础
线程发起
最简单的发起一个线程。
void thread_work(std::string str) {std::cout << "str: " << std << std::endl;
}
//初始化并启动一个线程
std::thread t1(thread, wangzn2016);
线程等待:
线程发起后,可能新的线程还没立即执行,或者还没执行完成,主线程就执行完了,此时子线程就会被回收,回收时会调用线程的析构函数,执行terminate操作。
因此,为了防止主线程退出或者局部作用域结束导致子线程被回收析构,我们可以通过join的方式,让主线程等待子线程执行完成并回收。
void thread_work(std::string str) {std::cout << "str: " << std << std::endl;
}
//初始化并启动一个线程
std::thread t1(thread, wangzn2016);
//主线程等待子线程执行完成
t1.join();
仿函数作为参数
也可以使用仿函数作为参数发起线程
class background_task {
public:void operator()() {std::cout << "wangzn2016" << str << std::endl;}
};
//这种方式会被编译器识别成函数"std::stread (*)(background_task) (*)()"
//std::thread t1(background_task());//其中一个解决办法就是,实例化一个对象后,再传入
//这里提供另外两种种解决办法
// 1.多加一层()
std::thread t2((background_task()));
t2.join();// 2.使用{}来初始化
std::thread t3{ background_task() };
t3.join();
Lambda表达式
lambda表达式也可以作为线程的参数传递给thread。
std::thread t1([](std::string str) {std::cout << "str: " << str << std::endl;
}, hellostr);t1.join();
线程detach
线程分离,也可以被称为守护线程。线程允许采用分离的方式在后台独自运行。
struct func {int& _i;func(int& i): _i(i){}void operator()() {for (int i = 0; i < 3; i++) {_i = i;std::cout << "_i: " << _i << std::endl;std::this_thread::sleep_for(std::chrono::seconds(1));}}
};void oops() {int some_local_state = 0;func myfunc(some_local_state);std::thread functhread(myfunc);//隐患,访问局部变量,局部变量可能会随着}结束而回收或随着主线程退出而回收functhread.detach();
}// detach 注意事项
oops();
//防止主线程退出过快,需要停顿一下,让子线程跑起来detach
std::this_thread::sleep_for(std::chrono::seconds(1));
上面的例子存在隐患,因为some_local_state
是局部变量, 当oops
调用结束后局部变量some_local_state
就可能被释放了,而线程还在detach后台运行,容易出现崩溃。
解决办法:
- 通过智能指针传递参数,因为引用计数会随着赋值增加,可保证局部变量在使用期间不被释放,这也就是我们之前提到的伪闭包策略。
- 将局部变量的值作为参数传递,这么做需要局部变量有拷贝复制的功能,而且拷贝耗费空间和效率。
- 将线程运行的方式修改为join,这样能保证局部变量被释放前线程已经运行结束。但是这么做可能会影响运行逻辑。 比如下面的修改
void use_join()
{ int some_local_state = 0; func myfunc(some_local_state); std::thread functhread(myfunc); functhread.join();
}
异常处理
void catch_exception() {int some_local_state = 0;func myfunc(some_local_state);std::thread functhread{ myfunc };try {//本线程做一些事情,可能引发崩溃std::this_thread::sleep_for(std::chrono::seconds(1));}catch (std::exception& e) {functhread.join();throw;}functhread.join();
}
但是用这种方式编码,会显得臃肿,可以采用RAII技术,保证线程对象析构的时候等待线程运行结束,回收资源。
class thread_guard {
private:std::thread& _t;
public:explicit thread_guard(std::thread& t):_t(t){}~thread_guard() {//join只能调用一次if (_t.joinable()) {_t.join();}}thread_guard(thread_guard const&) = delete;thread_guard& operator=(thread_guard const&) = delete;
};void auto_guard() {int some_local_state = 0;func my_func(some_local_state);std::thread t(my_func);thread_guard g(t);//本线程做一些事情std::cout << "auto guard finished " << std::endl;
}auto_guard();
慎用隐式类型转换
c++中会有一些隐式类型转换,比如char*转换为string等。这些隐式类型转换在线程的调用上可能会造成崩溃问题
void print_str(int i, std::string str)
{std::cout << "i: " << i << " std: " << str << std::endl;
}
void danger_oops(int som_param) {char buffer[1024];sprintf(buffer, "%i", som_param);//在线程内部将char const* 转化为std::stringstd::thread t(print_str, 3, buffer);t.detach();std::cout << "danger oops finished " << std::endl;
}
我们定义一个线程变量thread t
时,传递给这个线程的参数buffer
会被保存到thread
的成员变量中。 而在线程对象t内部启动并运行线程时,参数才会被传递给调用函数print_str
。 而此时buffer
可能随着}
运行结束而释放了。
解决的方式很简单,我们将参数传递给thread
时显示转换为string
就可以了, 这样thread
内部保存的是string
类型。
void safe_oops(int some_param) {char buffer[1024];sprintf(buffer, "%i", some_param);std::thread t(print_str, 3, std::string(buffer));t.detach();
}
引用参数
当我们创建的线程要调用的回调函数的参数为引用类型的时候,需要将参数显示转化为引用对象传递给线程的构造函数。
void change_param(int& param) {param++;
}void ref_oops(int some_param) {std::cout << "before change , param is " << some_param << std::endl;//需使用引用显示转换, std::refstd::thread t2(change_param, std::ref(some_param));t2.join();std::cout << "after change , param is " << some_param << std::endl;
}
因为线程的启动在底层是模板实现,并且嵌套多个函数,引用类型在传参的时候涉及到引用降级(引用折叠),最后传递给change_param函数的参数是个右值,因此会报错,所以我们需要使用引用显示转换, std::ref()来传参。
thread原理
template <class _Fn, class... _Args, enable_if_t<!is_same_v<_Remove_cvref_t<_Fn>, thread>, int> = 0>
_NODISCARD_CTOR explicit thread(_Fn&& _Fx, _Args&&... _Ax) {_Start(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...);
}
thread构造函数内部通过forward
原样转换传递给_Start
函数。关于原样转换的知识可以看我之前写的文章。 _Start
函数内部就是启动了一个线程_beginthreadex
执行回调函数。
template <class _Fn, class... _Args>void _Start(_Fn&& _Fx, _Args&&... _Ax) {using _Tuple = tuple<decay_t<_Fn>, decay_t<_Args>...>;auto _Decay_copied = _STD make_unique<_Tuple>(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...);constexpr auto _Invoker_proc = _Get_invoke<_Tuple>(make_index_sequence<1 + sizeof...(_Args)>{});#pragma warning(push)
#pragma warning(disable : 5039) // pointer or reference to potentially throwing function passed to// extern C function under -EHc. Undefined behavior may occur// if this function throws an exception. (/Wall)_Thr._Hnd =reinterpret_cast<void*>(_CSTD _beginthreadex(nullptr, 0, _Invoker_proc, _Decay_copied.get(), 0, &_Thr._Id));
#pragma warning(pop)if (_Thr._Hnd) { // ownership transferred to the thread(void) _Decay_copied.release();} else { // failed to start thread_Thr._Id = 0;_Throw_Cpp_error(_RESOURCE_UNAVAILABLE_TRY_AGAIN);}}
相关文章:
[C++并发编程] 线程基础
线程发起 最简单的发起一个线程。 void thread_work(std::string str) {std::cout << "str: " << std << std::endl; } //初始化并启动一个线程 std::thread t1(thread, wangzn2016); 线程等待: 线程发起后,可能新的线…...
基于若依框架和Vue2 + Element-UI 实现图片上传组件的重写与优化
背景 在使用 若依分离版Element-UI 的图片上传组件时,需要根据业务需求进行定制化处理,比如: 需要传递额外的业务参数到后端需要对上传路径进行修改需要对上传组件进行样式定制 实现步骤 1. 创建本地组件 首先在业务模块下创建本地的图片上传组件: src/views/xxx/compone…...
自定义类型: 结构体、枚举 、联合
目录 结构体 结构体类型的声明 匿名结构体 结构的自引用 结构体变量的定义和初始化 结构体成员变量的访问 结构体内存对齐 结构体传参 位段 位段类型的声明 位段的内存分配 位段的跨平台问题 位段的应用 枚举 枚举类型的定义 枚举的优点 联合体(共用体) 联合…...
力扣98:验证二叉搜索树
给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下: 节点的左 子树 只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。 示例 1: 输入…...
Java Stream reduce 函数,聚合数据
Stream.reduce() 是 Stream 的一个聚合方法,它可以把一个 Stream 的所有元素按照自定义聚合逻辑,聚合成一个结果。 先看一个简单数字求和: public class Main {public static void main(String[] args){int sum Stream.of(1, 2…...
npm install -g@vue/cli报错解决:npm error code ENOENT npm error syscall open
这里写目录标题 报错信息1解决方案 报错信息2解决方案 报错信息1 使用npm install -gvue/cli时,发生报错,报错图片如下: 根据报错信息可以知道,缺少package.json文件。 解决方案 缺什么补什么,这里我们使用命令npm…...
阿里云服务器(centos7.6)部署前后端分离项目(MAC环境)
Jdk17安装部署 下载地址:https://www.oracle.com/java/technologies/downloads/ 选择自己需要的jdk版本进行下载。 通过mac终端scp命令上传下载好的jdk17到服务器的/usr/local目录下 scp -r Downloads/jdk-17.0.13_linux-x64_bin.tar.gz 用户名服务器ip地址:/us…...
【机器学习】机器学习基础
什么是机器学习? 机器学习(Machine Learning, ML)是一种人工智能(AI)的分支,指计算机通过数据学习规律并做出预测或决策,而无需明确编程。它的核心目标是让机器能够从经验中学习,逐…...
BUUCTF—Reverse—Java逆向解密(10)
程序员小张不小心弄丢了加密文件用的秘钥,已知还好小张曾经编写了一个秘钥验证算法,聪明的你能帮小张找到秘钥吗? 注意:得到的 flag 请包上 flag{} 提交 需要用专门的Java反编译软件:jd-gui 下载文件,发现是个class文…...
基于JSP+MySQL的网上招聘系统的设计与实现
摘要 在这样一个经济飞速发展的时代,人们的生存与生活问题已成为当代社会需要关注的一个焦点。对于一个刚刚 踏入社会的年轻人来说,他对就业市场和形势了解的不够详细,同时对自己的职业规划也很模糊,这就导致大量的 时间被花费在…...
js 中 file 文件 应用
文章目录 文件上传File 对象基本属性文件上传大文件上传文件格式校验通过 type 属性校验图片格式通过文件名扩展名校验 文件解析一、处理图片文件流(以 Blob 格式接收文件流为例)二、处理文本文件流三、处理 PDF 文件流(借助 PDF.js 库来展示…...
Java 泛型详细解析
泛型的定义 泛型类的定义 下面定义了一个泛型类 Pair,它有一个泛型参数 T。 public class Pair<T> {private T start;private T end; }实际使用的时候就可以给这个 T 指定任何实际的类型,比如下面所示,就指定了实际类型为 LocalDate…...
「Mac畅玩鸿蒙与硬件33」UI互动应用篇10 - 数字猜谜游戏
本篇将带你实现一个简单的数字猜谜游戏。用户输入一个数字,应用会判断是否接近目标数字,并提供提示“高一点”或“低一点”,直到用户猜中目标数字。这个小游戏结合状态管理和用户交互,是一个入门级的互动应用示例。 关键词 UI互…...
自然语言处理期末试题汇总
建议自己做,写完再来对答案。答案可能存在极小部分错误,不保证一定正确。 一、选择题 1-10、C A D B D B C D A A 11-20、A A A C A B D B B A 21-30、B C C D D A C A C B 31-40、B B B C D A B B A A 41-50、B D B C A B B B B C 51-60、A D D …...
记录Threadlocal使用
编写ThreadLocal工具类 package com.jjking.jplan.context;public class BaseContext<T> {public static final ThreadLocal threadLocal new ThreadLocal();//存储用户public static void set(Object t) {threadLocal.set(t);}//获取用户public static <T> T ge…...
利用 SpringBoot 开发的新冠密接者跟踪系统:医疗机构疫情防控辅助方案
摘 要 信息数据从传统到当代,是一直在变革当中,突如其来的互联网让传统的信息管理看到了革命性的曙光,因为传统信息管理从时效性,还是安全性,还是可操作性等各个方面来讲,遇到了互联网时代才发现能补上自古…...
vue 2 父组件根据注册事件,控制相关按钮显隐
目标效果 我不注册事件,那么就不显示相关的按钮 注册了事件,才会显示相关内容 实现思路 组件在 mounted 的时候可以拿到父组件注册监听的方法 拿到这个就可以做事情了 mounted() {console.log(this.$listeners, this.$listeners);this.show.search !…...
【深度学习基础】一篇入门模型评估指标(分类篇)
🌈 个人主页:十二月的猫-CSDN博客 🔥 系列专栏: 🏀深度学习_十二月的猫的博客-CSDN博客 💪🏻 十二月的寒冬阻挡不了春天的脚步,十二点的黑夜遮蔽不住黎明的曙光 目录 1. 前言 2. 模…...
hls视频流学习
hls格式播放的依赖安装: <!-- 新增hls播放库 -->npm install hls.js 组件封装: <template><div class"hls-player-cls"><video ref" video" controls style"width: 100%; max-width: 800px;">…...
【electron-vite】搭建electron+vue3框架基础
一、拉取项目 electron-vite 中文文档地址: https://cn-evite.netlify.app/guide/ 官网网址:https://evite.netlify.app/ 版本 vue版本:vue3 构建工具:vite 框架类型:Electron JS语法:TypeScript &…...
第三方Express 路由和路由中间件
文章目录 1、Express 应用使用回调函数的参数: request 和 response 对象来处理请求和响应的数据。2、Express路由1.路由方法2.路由路径3.路由处理程序 3. 模块化路由4. Express中间件1.中间件简介2.中间件分类3.自定义中间件 1、Express 应用使用回调函数的参数&am…...
WPF 常用的5个布局容器控件介绍
1. Grid Grid 是最常用的布局容器之一,它允许开发者以表格的方式对控件进行组织和布局。Grid 使用行和列来划分区域,可以精确控制控件的位置和大小。 特点: 行列定义:Grid 使用 RowDefinitions 和 ColumnDefinitions 来定义行和…...
【JAVA] 杂谈: java中的拷贝(克隆方法)
这篇文章我们来介绍什么是拷贝,并且实现浅拷贝到深拷贝。 目录 一、浅拷贝 1.1 clone 方法 1.2 实现浅拷贝: 1.2.1 重写 clone方法 1.2.2 实现接口 Cloneable 1.2.3 调用克隆方法 1.2.4 原理图: 1.3 浅拷贝的不足 1.3.1 增加引用…...
同时多平台git配置:GitHub和Gitee生成不同的SSH Key
文章目录 GitHub和Gitee生成不同的SSH Key步骤1:生成SSH Key步骤2:配置SSH配置文件步骤3:查看SSH公钥步骤4:将SSH公钥添加到GitHub和Gitee步骤5:测试SSH连接步骤6:添加remote远程库 GitHub和Gitee生成不同的…...
flink1.6集成doris,并从mysql同步数据到doris
使用 Apache Flink 1.6 集成 Doris,并从 MySQL 同步数据到 Doris 是一个复杂的任务,但可以通过以下步骤实现。Doris 是一个现代化的 MPP(大规模并行处理)SQL 数据库,支持实时分析和交互式查询。Flink 可以作为实时数据…...
手搓一个不用中间件的分表策略
场景:针对一些特别的项目,不用中间件,以月为维度进行分表,代码详细设计方案 1. 定义分片策略 首先,定义一个分片策略类,用于决定数据存储在哪个分表中 import java.time.LocalDate; import java.time.fo…...
AI前景分析展望——GPTo1 SoraAI
引言 人工智能(AI)领域的飞速发展已不仅仅局限于学术研究,它已渗透到各个行业,影响着从生产制造到创意产业的方方面面。在这场技术革新的浪潮中,一些领先的AI模型,像Sora和OpenAI的O1,凭借其强大…...
损失函数Hinge Loss介绍
Hinge Loss 是一种损失函数,广泛用于 支持向量机(SVM, Support Vector Machine) 和一些分类问题中。它特别适合用于 二分类问题,主要目标是让模型的预测值(通常是经过线性变换的原始分数)与真实标签之间的间隔尽可能大,从而提高分类的鲁棒性。 Hinge Loss 的定义 Hinge…...
多维高斯分布(Multivariate Gaussian Distribution)以及协方差矩阵:解析与应用
多维高斯分布:全面解析及其应用 1. 什么是多维高斯分布? 多维高斯分布(Multivariate Gaussian Distribution),也称多元正态分布,是高斯分布在高维空间中的推广。它描述了随机向量 ( x ( x 1 , x 2 , … ,…...
前端开发常用快捷键
浏览器 ctrl e 光标定位在搜索框ctrl r 刷新ctrl t 新打开tabctrl tab 向右切换tabctrl shift tab 向左切换tab vscode ctrl p 全局搜索文件ctrl f 当前文件搜索alt 光标左键向下拖动:竖向选中多行文本ctrl b 切换侧边栏显示隐藏ctrl shift p 显示命…...
用MATLAB符号工具建立机器人的动力学模型
目录 介绍代码功能演示拉格朗日方法回顾求解符号表达式数值求解 介绍 开发机器人过程中经常需要用牛顿-拉格朗日法建立机器人的动力学模型,表示为二阶微分方程组。本文以一个二杆系统为例,介绍如何用MATLAB符号工具得到微分方程表达式,只需要…...
全面解析 MySQL 常见问题的排查与解决方法
目录 前言1. 查看 MySQL 日志信息1.1 日志文件的种类与路径1.2 查看日志内容的方法1.3 日志分析的关键点 2. 查看 MySQL 服务状态2.1 查看服务状态2.2 检查进程运行情况2.3 常见启动失败问题与解决 3. 检查 MySQL 配置信息3.1 配置文件的路径与内容3.2 验证配置文件的正确性 4.…...
泷羽Sec-星河飞雪-BurpSuite之解码、日志、对比模块基础使用
免责声明 学习视频来自 B 站up主泷羽sec,如涉及侵权马上删除文章。 笔记的只是方便各位师傅学习知识,以下代码、网站只涉及学习内容,其他的都与本人无关,切莫逾越法律红线,否则后果自负。 泷羽sec官网:http…...
【小白学机器学习34】基础统计2种方法:用numpy的方法np().mean()等进行统计,pd.DataFrame.groupby() 分组统计
目录 1 用 numpy 快速求数组的各种统计量:mean, var, std 1.1 数据准备 1.2 直接用np的公式求解 1.3 注意问题 1.4 用print() 输出内容,显示效果 2 为了验证公式的背后的理解,下面是详细的展开公式的求法 2.1 均值mean的详细 2.2 方差…...
【C++】stack和queue
目录 1. stack的介绍和使用 1.1 stack的介绍 1.2 stack的使用 2. queue的介绍和使用 2.1 queue的介绍 2.2 queue的使用 3. 容器适配器 3.1 什么是适配器 3.2 STL标准库中stack和queue的底层结构 3.3 deque的简单介绍(了解) 3.3.1 deque的原理介绍 3.3.2 deque优势与…...
向量的内积和外积 为什么这样定义
向量的内积和外积 为什么这样定义 flyfish 定义、公理与证明的区别 定义: 定义是人为规定的,用于描述概念的含义。例如,内积和外积是根据实际需求定义的,目的是描述几何和代数性质。定义不需要证明。 公理: 公理是数…...
简述循环神经网络RNN
1.why RNN CNN:处理图像之间没有时间/先后关系 RNN:对于录像,图像之间也许有时间/先后顺序,此时使用CNN效果不会很好,同理和人类的语言相关的方面时间顺序就更为重要了 2.RNN和CNN之间的关联 RNN和CNN本质上其实一…...
【Electron学习笔记(四)】进程通信(IPC)
进程通信(IPC) 进程通信(IPC)前言正文1、渲染进程→主进程(单向)2、渲染进程⇌主进程(双向)3、主进程→渲染进程 进程通信(IPC) 前言 在Electron框架中&…...
APP自动化测试框架的开发
基于appium的APP自动化测试框架的开发流程概览 1. 环境搭建 安装Appium Server 下载与安装:可以从Appium官方网站(Redirecting)下载安装包。对于Windows系统,下载.exe文件后双击安装;对于Mac系统,下载.dmg…...
【深度学习】各种卷积—卷积、反卷积、空洞卷积、可分离卷积、分组卷积
在全连接神经网络中,每个神经元都和上一层的所有神经元彼此连接,这会导致网络的参数量非常大,难以实现复杂数据的处理。为了改善这种情况,卷积神经网络应运而生。 一、卷积 在信号处理中,卷积被定义为一个函数经过翻转…...
pytorch 融合 fuse 学习笔记
目录 fuse_lora 作用是什么 fuse_modules源码解读 fuse_lora 作用是什么 在深度学习模型微调场景下(与 LoRA 相关) 参数融合功能 在使用 LoRA(Low - Rank Adaptation)对预训练模型进行微调后,fuse_lora函数的主要作…...
41 基于单片机的小车行走加温湿度检测系统
目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于51单片机,采样DHT11温湿度传感器检测温湿度,滑动变阻器连接数码转换器模拟电量采集传感器, 电机采样L298N驱动,各项参数通过LCD1602显示&#x…...
GitLab: You cannot create a branch with a SHA-1 or SHA-256 branch name
最近在迁移git库,把代码从gerrit迁移到gitlab,有几个库报错如下: GitLab: You cannot create a branch with a SHA-1 or SHA-256 branch name ! [remote rejected] refs/users/73/373/edit-95276/1 -> refs/users/73/373/edit-95276/1 (p…...
YOLOv9改进,YOLOv9引入TransNeXt中的ConvolutionalGLU模块,CVPR2024,二次创新RepNCSPELAN4结构
摘要 由于残差连接中的深度退化效应,许多依赖堆叠层进行信息交换的高效视觉Transformer模型往往无法形成足够的信息混合,导致视觉感知不自然。为了解决这个问题,作者提出了一种聚合注意力(Aggregated Attention),这是一种基于仿生设计的token混合器,模拟了生物的中央凹…...
TorchMoji使用教程/环境配置(2024)
TorchMoji使用教程/环境配置(2024) TorchMoji简介 这是一个基于pytorch库,用于将文本分类成不同的多种emoji表情的库,适用于文本的情感分析 配置流程 从Anaconda官网根据提示安装conda git拉取TorchMoji git clone https://gi…...
uniapp运行时,同步资源失败,未得到同步资源的授权,请停止运行后重新运行,并注意手机上的授权提示。
遇到自定义基座调试时安装无效或无反应?本文教你用 ADB 工具快速解决:打开 USB 调试,连接设备,找到应用包名,一键卸载问题包,清理干净后重新运行调试基座,轻松搞定! 问题场景&#…...
uniapp中父组件调用子组件方法
实现过程(setup语法糖形式下) 在子组件完成方法逻辑,并封装。在子组件中使用defineExpose暴露子组件的该方法。在父组件完成子组件ref的绑定。通过ref调用子组件暴露的方法。 子组件示例 <template> </template><script se…...
腾讯云 AI 代码助手:单元测试应用实践
引言 在软件开发这一充满创造性的领域中,开发人员不仅要构建功能强大的软件,还要确保这些软件的稳定性和可靠性。然而,开发过程中并非所有任务都能激发创造力,有些甚至是重复且乏味的。其中,编写单元测试无疑是最令人…...
ArcGIS栅格影像裁剪工具
1、前言 在最近的栅格转矢量处理过程中,发现二值化栅格规模太大,3601*3601,并且其中的面元太过细碎,通过arcgis直接栅格转面有将近几十万的要素,拿这样的栅格数据直接运行代码,发现速度很慢还难以执行出来结…...
VueWordCloud标签云初实现
文章目录 VueWordCloud学习总结安装初使用在组件中注册该组件简单使用项目中实现最终实现效果 VueWordCloud学习总结 本次小组官网的项目中自己要负责标签模块,想要实现一个标签云的效果,搜索了很多,发现vue有一个VueWordCloud库,…...