02C++11多线程编程之detach传参详解

2023-11-04

02C++11多线程编程之detach传参详解

这篇文章将介绍我们在使用detach时如何传参给子线程。

1 detach传参为引用

#include<iostream>
#include<thread>
#include<string>

using namespace std;


void myprint1(const int &i){

	//打印i的地址看是否是引用
	cout << &i << endl;

	cout << i << endl;

}

int main(){

	int mvar = 1;
	cout << &mvar << endl;

	thread myobj(myprint1, mvar);

	myobj.detach();


	cout << "主线程执行!" << endl;

	return 0;
}

结果分析:
为了防止主线程结束导致detach的子线程无法输出,我们在主线程打个断点或者睡眠一下,结果可以看到,上面代码即使是传引用,形参i的值为拷贝值。
在这里插入图片描述

2 detach传参为指针

#include<iostream>
#include<thread>
#include<string>

using namespace std;


void myprint1(char *p){

	cout << p << endl;

}

int main(){

	char buf[] = "I am bb";

	thread myobj(myprint1, buf);

	myobj.detach();


	cout << "主线程执行!" << endl;

	return 0;
}

结果:由于打印地址首地址会直接输出字符串,所以我们截图说明。可以看到,detach传参为地址时,不会产生拷贝,所以detach使用指针是万万不可的。
在这里插入图片描述

3 深度解析字符串传参问题
1)那么有人就会说,既然detach传参为引用也会拷贝,我传字符串时detach的形参写成string的引用不就行了吗?不传string是因为引用也会拷贝,所以避免拷贝两次。
所以继续测试。

#include<iostream>
#include<thread>
#include<string>

using namespace std;

//detach线程的参数必须加sonst,否则编译器会让你报错
void myprint1(const string &p){

	cout << p << endl;

}

int main(){

	char buf[] = "I am bb";

	thread myobj(myprint1, buf);

	myobj.detach();


	cout << "主线程执行!" << endl;

	return 0;
}

结果:
右击p选择快速监视,获取string的地址。
在这里插入图片描述
buf的地址。
在这里插入图片描述

可以看到上面两张图,确实是不同的值,即发生了拷贝。那么这样的字符串传参就确保程序没有问题了吗?实际上这样也并非是安全的。我们知道detach传参引用会进行拷贝,那么什么时候拷贝呢?当主线程结束了,但是这个拷贝并没来得及的话就会造成程序出错。
并且这也引出不仅是字符串,其它类型也一样,只要主线程结束在拷贝之前,程序都是不安全的。
所以继续测试字符串的传参问题。

2)在传字符串引用时,如何防止主线程结束前完成detach的拷贝动作。并且
这里先直接告诉答案:就是主线程传实参时,传匿名对象即可,可以确保主线程结束之前完成detach的拷贝。

下面开始验证:
首先先调用join查看稳定的程序写法,以便对比。

#include<iostream>
#include<thread>
#include<string>

using namespace std;

class A{
public:
	int m_i;

public:
	//类型转换构造函数,可以把一个int转换成一个类A对象。
	A(int a) :m_i(a) { cout << "A::A(int a)构造函数执行!" << endl; }
	A(const A &a) :m_i(a.m_i) { cout << "A::A(A &a)复制构造函数执行!" << endl; }

	~A() { cout << "A::~A()析构函数执行!" << endl; }
};

void myprint(const int i, const A &pmybuf){
	// 打印pmybuf对象的地址
	cout << &pmybuf << endl;
}

int main(){

	int mvar = 1;
	int mysecondpar = 12;

	thread myobj(myprint, mvar, mysecondpar);//我们希望mysecondpar转成A类型对象传递给myprint的第二个参数

	if (myobj.joinable()) {
		myobj.join();
		//myobj.detach();
	}

	cout << "主线程执行!" << endl;

	return 0;
}

结果:我们可以看到结果是正常的,子线程首先调用拷贝构造,然后打印用int类型转换构造函数创建的对象地址,最后析构。
在这里插入图片描述

把上面程序的join换成detach,并且主线程的打印也去掉,让其快速结束,继续测试。
结果,多运行几次,可以看到,主线程是有可能比拷贝动作先结束的,所以就可能存在危险。
在这里插入图片描述

正确方法:实参改变为传临时对象。并且打印对应的线程id。
同理,我们先给出下列代码的join稳定时的现象,方便观察。

#include<iostream>
#include<thread>
#include<string>

using namespace std;

class A{
public:
	int m_i;

public:
	//类型转换构造函数,可以把一个int转换成一个类A对象。
	A(int a) :m_i(a){
		cout << "A::A(int a)构造函数执行!" << " this=" << this << " threadid:" << std::this_thread::get_id() << endl;
	}
	A(const A &a) :m_i(a.m_i){
		cout << "A::A(A &a)复制构造函数执行!" << " this=" << this << " threadid:" << std::this_thread::get_id() << endl;
	}

	~A(){
		cout << "A::~A()析构函数执行!" << " this=" << this << " threadid:" << std::this_thread::get_id() << endl;
	}
};

void myprint2(const A &pmybuf){

	cout << "子对象myprint的参数地址是" << &pmybuf << " threadid" << std::this_thread::get_id() << endl;// 打印的是pmybuf对象的地址

}

int main(){

	cout << "主线程id:" << std::this_thread::get_id() << endl;

	int  mvar = 2;
	//thread myobj(myprint2, mvar);  //致命问题是在子线程中构造A类对象
	thread myobj(myprint2, A(mvar)); //用了临时对象后,所有的A类对象都在main()函数中已经构造完毕了,故detach不再惧怕主线程先结束
	if (myobj.joinable()) {
		myobj.join();
		//myobj.detach();
	}
 

	return 0;
}

以下是join的稳定代码输出结果,传参为匿名对象时,所有的对象拷贝均在主线程中完成(注意不是匿名对象是在子线程中构造),后面的析构因为竞争打印得比较乱,但是根据threadid来看,子线程后析构。
在这里插入图片描述

由上面的join结论可以得出,传匿名对象时,所有的构造均在主线程结束之前完成,所以将上面的代码join换成detach之后,多次执行,是不怕出现任何问题的,detach结果如下:
在这里插入图片描述

所以根据上面三点可以先总结一下detach的传参问题:

  • 1)int型的直接值传递。
  • 2)指针绝不使用。
  • 3)传递类对象时,形参为引用,实参为匿名对象。形参为引用是防止多次拷贝,实参为匿名对象是防止子线程拷贝之前主线程提前结束。
  • 4)使用join上面问题均不出现。

4 传进真正的引用进detach时的形参
我们知道,不管join(看上图)还是detach,线程函数的形参即使写成引用形式也是无法获取引用的,照样会拷贝,那么在join时(detach不能传真正的引用)如何传递真正的引用给线程函数使用呢?
那就是std::ref()引用函数了。注意:detach决不能使用该函数,主线程结束后会出错。所以说std::ref()是为join量身定做的。

#include<iostream>
#include<thread>
#include<string>

using namespace std;

class A{
public:
	int m_i;
	//mutable int m_i;由于在C++11线程回调的函数中,形参为类对象时需要加上const才能确保语法正确,这样就不能修改到该对象值了,添加mutable即可。
public:
	//类型转换构造函数,可以把一个int转换成一个类A对象。
	A(int a) :m_i(a){
		cout << "A::A(int a)构造函数执行!" << " this=" << this << " threadid:" << std::this_thread::get_id() << endl;
	}
	A(const A &a) :m_i(a.m_i){
		cout << "A::A(A &a)复制构造函数执行!" << " this=" << this << " threadid:" << std::this_thread::get_id() << endl;
	}

	~A(){
		cout << "A::~A()析构函数执行!" << " this=" << this << " threadid:" << std::this_thread::get_id() << endl;
	}
};

void myprint2(const A &pmybuf){

	cout << "子对象myprint的参数地址是" << &pmybuf << " threadid" << std::this_thread::get_id() << endl;// 打印的是pmybuf对象的地址

}

int main(){

	cout << "主线程id:" << std::this_thread::get_id() << endl;

	A mvar(2);
	thread myobj(myprint2, ref(mvar));//使join获取真正的引用,为join量身定做
	if (myobj.joinable()) {
		myobj.join();
		//myobj.detach();//决不能使用detach
	}
 

	return 0;
}

结果可以看到,构造函数只执行了一次。
在这里插入图片描述

5 传递智能指针作为线程参数
这个没什么好讲的,只是需要注意一点,独占型的智能指针需要使用移动语义进行传参。

#include<iostream>
#include<thread>
#include<string>

using namespace std;

void myprint3(unique_ptr<int> pzn){
	cout << "unique_ptr 传参" << endl;
}

int main(){

	unique_ptr<int> myp(new int(100));
	thread myobj(myprint3, std::move(myp));//unique_ptr为独占型智能指针,不使用move传参会编译报错,凡是unique_pte传参都需要这样
	if (myobj.joinable()) {
		myobj.join();
		//myobj.detach();//决不能使用detach,因为new的内存在主线程中,虽然一开始共用同一进程的堆,
		//但是主线程结束后,子线程被系统的某个进程回收,共享堆改变,会出现未知问题
	}

	return 0;
}

6 用成员函数指针做线程函数
成员函数作线程函数时,写法比较奇怪。平常的普通回调函数直接传函数名即可,而成员函数需要取地址符并且声明是那个类,后面为形参,这里注意:由于成员函数有this指针,所以传成员函数的第二个参数需为该类对象地址。直接看代码。

#include<iostream>
#include<thread>
#include<string>

using namespace std;

class A{
public:
	mutable int m_i;

public:
	//类型转换构造函数,可以把一个int转换成一个类A对象。
	A(int a) :m_i(a){
		cout << "A::A(int a)构造函数执行!" << this << "threadid:" << std::this_thread::get_id() << endl;
	}
	A(const A &a) :m_i(a.m_i){
		cout << "A::A(A &a)复制构造函数执行!" << this << "threadid:" << std::this_thread::get_id() << endl;
	}

	~A(){
		cout << "A::~A()析构函数执行!" << this << "threadid:" << std::this_thread::get_id() << endl;
	}

	void thread_work(int num){
		cout << "子线程thread——work执行!" << this << "threadid:" << std::this_thread::get_id() << endl;
	}
};

int main(){

	A myobj(10);
	//thread mytobj(&A::thread_work, myobj, 15);
	thread mytobj(&A::thread_work, &myobj, 15);//join时参2可以使用地址传参,让其只构造一次。也可调用ref
	//当成员函数为括号重载时可以这样写thread mytobj(myobj, 15);或者thread mytobj(std::ref(myobj), 15);但thread mytobj(&myobj, 15);编译器让你报错
	mytobj.join();

	return 0;
}

结果搞定,成功运行。
在这里插入图片描述

7 大总结detach的传参问题
也就是我能第三点的总结。

  • 1)int型的直接值传递。
  • 2)指针绝不使用。
  • 3)传递类对象时,形参为引用,实参为匿名对象。形参为引用是防止多次拷贝,实参为匿名对象是防止子线程拷贝之前主线程提前结束。
  • 4)使用join上面问题均不出现。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

02C++11多线程编程之detach传参详解 的相关文章

  • 集群():是否可以仅检查文件是否已锁定,而不实际获取锁定(如果没有)?

    我的用例如下 我有一个程序 它强制在任何给定时间只能运行它的一个实例 因此在启动时它总是尝试在标准位置获取锁定文件 并在该文件终止时终止已经被锁定 这一切都工作正常 但现在我想用一个新的命令行选项来增强程序 当指定该选项时 将导致程序只打印
  • std::map find 在 C++ 中不起作用[重复]

    这个问题在这里已经有答案了 我使用以下几行创建了一个哈希映射和一个迭代器 std map
  • StackExchange Redis 删除所有以以下开头的键

    我有一个格式的密钥 Error 1 Error 24 Error 32 Using StackExchange Redis 我该怎么办KeyDelete在与格式匹配的所有键上Error 在另一个答案中我看到了 LUA 脚本 EVAL ret
  • 如何获取枚举数作为常量?

    From 枚举中定义的项目总数 https stackoverflow com questions 856154 total number of items defined in an enum 我发现我可以使用以下方法获取枚举数 Enum
  • 没有配置身份验证处理程序来处理该方案

    这是一个非常烦人的问题 我在我的 asp net core 项目上设置 cookie 身份验证 有时会出现此错误 有时不会 没有图案 它只是开始抛出错误 然后突然停止 然后再次开始 例外情况是 InvalidOperationExcepti
  • 如何反序列化 XML 文档

    如何反序列化此 XML 文档
  • 以编程方式更新 Wifi 网络

    我正在尝试创建一个程序 当某个 wifi 网络在范围内时 该程序会连接到该网络 即使已经连接到另一个 wifi 也是如此 我在用着简单Wifi https github com DigiExam simplewifi 基本上效果很好 除了在
  • 为什么 xcode IDE 认为 `friend` 是保留字

    我一直在开发一个个人项目 并在我创建的新类中包含以下代码 property readonly getter isFriend BOOL friend 它似乎没有任何问题 当我构建它时 它可以编译得很好 但是当我们在xcode IDE看起来像
  • 将两个垂直滚动条相互绑定

    我在控件中有两个 TextBox 并且它们都有两个 VerticalScrollBar 我想在它们之间绑定 VerticalScrollBars 如果一个向上 第二个也会向上等等 如果可以的话我该怎么做 Thanks 不是真正的绑定 但它有
  • 我想找到 C# 代码中所有后面没有括号的 if 语句。通过正则表达式

    我想找到所有if声明和for后面没有大括号的语句 当你在一个文件中写入一行时if声明您大多不会将其括在大括号中 所以我想找到所有这些if and for声明 请帮忙 就像我想捕捉这个声明 if childNode Name B return
  • 绑定集合的子集

    我有一个ObservableCollection
  • Qt 多重继承和信号

    由于 QObject 我在 QT 中遇到了有关多重继承的问题 我知道很多人也有同样的问题 但我不知道该如何解决 class NavigatableItem public QObject Q OBJECT signals void desel
  • 如何使用 itextsharp 更改 PDF 公式的按钮图标?

    我目前正在尝试使用 itextsharp 填写预定义的表单 除了添加图像之外 一切正常 这之前已经在 Adob e 的 FDF 工具包中运行过 该工具包已编译为 NET 1 1 这不再适用于 NET 4 0 我改用了 itextsharp
  • Qt - 添加超链接到对话框

    有没有办法在 Qt 对话框中添加可点击的超链接 IE 它应该看起来像一个超链接 蓝色文本 当您单击它时 它应该在浏览器中打开该超链接 像这样的东西 Use QLabel setOpenExternalLinks bool 并在标签上设置文本
  • ArrayList 有什么问题?

    最近我问了一个关于 SO 的问题 其中提到了可能使用 c ArrayList 来解决问题 有人评论说使用数组列表不好 我想了解更多有关此的信息 我以前从未听说过关于数组列表的这种说法 有人可以带我了解使用数组列表可能出现的性能问题吗 C n
  • TCP/IP 传输期间套接字数据损坏

    当我通过预连接的 TCP IP 套接字发送数据时 我发现数据已损坏 Example Station1 正在向 Station2 发送数据 我已经在发送之前 在 S1 和接收之后 在 S2 打印了数据 以下是消息 S1 发送的数据是ACKS2
  • asio::this_coro::executor 的实现是什么

    在协程函数中 我们可以添加auto ex co await asio this coro executor 获取该协程的执行者 但当我想了解它的定义时 我发现了这个 Awaitable type that returns the execu
  • 为什么在一行中使用这个 C++ 函数两次会导致编译错误?

    我在尝试在 Visual C 2010 中实现智能相等测试宏类型模板函数时遇到了一些麻烦 该函数与VS 中关于模板函数默认参数的错误 https stackoverflow com questions 10343177 why do i g
  • 在派生类中访问基类变量

    class Program static void Main string args baseClass obj new baseClass obj intF 5 obj intS 4 child obj1 new child Consol
  • 获取线段上最接近另一个点的点[关闭]

    Closed 这个问题需要调试细节 help minimal reproducible example 目前不接受答案 我想找到线段AB上最接近另一个点P的点 我的想法是 Get a1 and b1由直线公式y1 a1x b1 使用 A 点

随机推荐

  • QT信号与槽的特点和用法

    1 概念 信号 Signal 就是在特定情况下被发射的事件 例如 PushButton 最常见的信号就是鼠标单 击时发射的 clicked 信号 槽 Slot 就是对信号响应的函数 槽就是一个函数 与一般的 C 函数是一样的 可以定义在类的
  • nested exception is org.apache.ibatis.binding.BindingException

    nested exception is org apache ibatis binding BindingException Parameter roleIdList not found Available parameters are 0
  • Date转换成LocalDateTime类型

    1 先new 一个当前时间 2 获取instant和zoneId 3 将instant和zoneId塞进LocalDateTime ofInstant这个方法里面 4大功告成 我也是今天碰到记录一下 省的以后在找
  • 生信学习——基于R的可视化习题30个(附详细答案解读)

    题目目录 一 基础绘图 1 对RNAseq expr的每一列绘制boxplot图 2 对RNAseq expr的每一列绘制density图 3 对RNAseq expr的每一列绘制条形图 4 对RNAseq expr的每一列取log2后重新
  • Qt串口调试助手

    串口通信因为其协议简单 应用灵活 被广泛使用 Qt自带了串口通信模块QtSerialPort 功能齐全 使用起来非常方便 QtSerialPort模块中有2个类使用的最多 分别是QSerialPort类和QSerialPortInfo类 Q
  • selenium-server-standalone.jar无法启动

    java jar selenium server standalone 2 48 0 jar htmlSuite firefox http localhost 8080 F selenium selenium 2 41 0 login su
  • idea默认文件类型关联

    idea在文件创建的时候没有指定后缀或关联成文本 解决 Editor gt File Types gt Text
  • 【YOLOv8/YOLOv7/YOLOv5/YOLOv4/Faster-rcnn系列算法改进NO.64】即插即用新的注意力机制RFAConv

    前言 作为当前先进的深度学习目标检测算法YOLOv8 已经集合了大量的trick 但是还是有提高和改进的空间 针对具体应用场景下的检测难点 可以不同的改进方法 此后的系列文章 将重点对YOLOv8的如何改进进行详细的介绍 目的是为了给那些搞
  • [译] LLVM IR中间码指令集

    最近在研究这个东西 实在百度不到 最后在谷歌上找到英文手册 英文手册地址 https llvm org docs LangRef html 先说点废话 国内的IT水平离世界顶级水平差距实在有点大 个人感觉 有些因素导致这个结果 1 化身广告
  • 控制工程学习2

    前言 此文为个人学习笔记 所学习的视频链接 1 https www bilibili com video BV1hV411v7KB p 29 share source copy web 2 https www bilibili com vi
  • 快速实现Lora通信(一对多)详解

    TOC 一 Lora介绍 Lora不是一种协议 而是一种基于扩频通信的射频技术 它相对于其他射频技术比如2 4G或普通433 915M来讲 具有更远的传输距离 几千米 和更强的越障性能 所以我们把一个Lora设备作为中心点 多个Lora设备
  • https://www.cnblogs.com/wenwenli/p/8301975.html

    https www cnblogs com wenwenli p 8301975 html 单线程 单进程 非阻塞 事件驱动 句柄就是函数 事件驱动模型 事件源 触发事件 监听事件程序 事件处理程序 路径的要求不严格 node的知识点 1
  • 使用 WSL 在 Windows 上安装 Linux 编程

    使用 WSL 在 Windows 上安装 Linux 编程 近年来 随着开源软件的盛行和跨平台开发的需求不断增长 Windows Subsystem for Linux WSL 成为了一个备受瞩目的工具 WSL 允许我们在 Windows
  • pycharm设置中文,字体颜色修改,设置背景图片(图文讲解)

    目录 1 设置中文 2 设置背景图片 3 字体大小 颜色的修改 这个是我的pycharm编译器的样子 好看吧 pycharm在安装之后默认是英语的 有些小伙伴的英语不太会 看英文会比较吃力 这里我来教大家怎么去设置中文 以及字体颜色大小 背
  • 客户端client 突然断电了,那么服务器如何快速的知道呢

    1 使用定时器 适合有数据流动的情况 2 使用socket选项SO KEEPALIVE 适合没有数据流动的情况 心跳包技术 心跳包之所以叫心跳包是因为 它像心跳一样每隔固定时间发一次 以此来告诉服务器 这个客户端还活着 事实上这是为了保持长
  • elementui el-tree多层级数据递归根据条件筛选(禁用指定节点)

    项目中用的是elementui的tree组件 要实现的效果是根据关键字去筛选过滤出禁用的节点 所以首先需要的是对树结构数据进行整合 export function deepFilter3 nodes key query Array isAr
  • Android WebView 的使用

    WebView webView WebView findViewById R id webView webView loadUrl http 192 168 1 3 8080 action stream webView setWebView
  • 程序员如何写好一篇技术文章?

    结合自身写作经历以及本次训练营直播分享的内容 谈一谈写作技巧以及程序员如何写好一篇技术文章 1 写作训练营回顾 2020 年 12 月 26 日下午 我参加了一个写作训练营的直播 活动内容如下 该训练营是以直播的方式开展 四位嘉宾给大家分享
  • 图像识别ImageRecognition

    图像识别 注意 cv2中的色彩排列是 b g r 而matplotlib库中的排列方式是 r g b 本文件中采用plt进行输出 因此颜色不是准确的颜色 1 OpenCV 机器视觉 import cv2 as cv import numpy
  • 02C++11多线程编程之detach传参详解

    02C 11多线程编程之detach传参详解 这篇文章将介绍我们在使用detach时如何传参给子线程 1 detach传参为引用 include