01C++11多线程编程之thread,join,detach,joinable以及简说detach传引用地址的大坑

2023-11-12

01C++11多线程编程之thread,join,detach,joinable以及简说detach传引用地址的大坑

1 thread类对象创建线程与join回收线程

  • 1)thread创建线程很简单,定义一个对象然后传一个可调用对象即可。
    可调用对象:普通的回调函数,类内重载括号()运算符传类对象,lambda表达式,packed_task模板类打包任务等等都可以。

  • 2)线程回收join的原因:主要目的是回收进程控制块PCB,保存了一些线程函数返回值等内容,若不回收可能top查看VIRT虚拟内存和RES进程所占内存比较大,因为一般开启一个线程系统会开辟一个64M的堆栈空间。

代码:

#include<iostream>
#include<thread>

using namespace std;

void myprint(){

	cout << "线程开始执行了!" << endl;
	cout << "线程执行结束了!" << endl;

}
int main(){

	//1 创建线程,myprint为可调用对象。
	thread myobj(myprint);
	
	//2 join函数的作用:阻塞在调用者线程,等待回收子线程资源。
	myobj.join();

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

	return 0;
}

在这里插入图片描述

2 detach分离线程
detach的作用是使线程分离,分离后当主线程结束后交由系统管理,具体可去看我僵尸进程的讲述。当创建thread对象后,要么调用join要么调用detach,不能同时调用(我试的时候编译没错,执行完后报错)。
不建议使用detach,当然必须要用的话也可以,注意一下即可。

1)看下列使用detach例子代码。

#include<iostream>
#include<thread>

using namespace std;

void myprint(){

	cout << "1!" << endl;
	cout << "2!" << endl;
	cout << "3!" << endl;
	cout << "4!" << endl;
	cout << "5!" << endl;
	cout << "6!" << endl;

}
int main(){

	//1 创建线程,myprint为可调用对象。
	thread myobj(myprint);
	
	//2 join函数的作用:阻塞在调用者线程,等待回收子线程资源。
	//myobj.join();

	//3 detach,线程分离
	myobj.detach();

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

	return 0;
}

上面代码执行结果分析:下面看到,子线程中打印了6条数字,但屏幕却显示了3条,这是因为主线程结束了,子线程的宿主线程死亡,系统会将该子线程挂载到一个新的主线程,方便回收防止产生僵尸进程。
这样的话,由于此时子线程与已经结束的主线程的屏幕已经毫无关系,所以子线程剩余的打印内容并不会输出到屏幕上。所以detach之后一般称为后台任务,注意,虽然没有打印完整,但是程序还是正常执行完毕的,你的代码逻辑还是会正常工作,所以不必担心,只是让你知道这种情况而已。

如果你想让子线程的内容完整打印到屏幕,可以让主线程不结束即可,例如睡眠5s。
在这里插入图片描述

3 joinable判断是否可以join或者detach
joinable的作用:返回true:代表可以join或者detach;否则说明已经join或者detach,不能再调用。

#include<iostream>
#include<thread>

using namespace std;

void myprint(){

	cout << "1!" << endl;
	cout << "2!" << endl;
	cout << "3!" << endl;
	cout << "4!" << endl;
	cout << "5!" << endl;
	cout << "6!" << endl;

}
int main(){

	//1 创建线程,myprint为可调用对象。
	thread myobj(myprint);
	
	//2 join函数的作用:阻塞在调用者线程,等待回收子线程资源。
	//myobj.join();
	if (myobj.joinable()) {
		myobj.join();
		//myobj.detach();
		cout << "可以join或者detach" << endl;

	}
	else {
		cout << "不能再join或者detach" << endl;
	}

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

	return 0;
}

结果正确。
在这里插入图片描述

4 detach传参为可调用对象,并且参数存在为引用或者地址的问题(detach传引用地址的大坑)
这里先简单介绍当detach的线程函数传可调用对象时容易出现的错误。记住,第四点的问题皆因调用detach才会引起的问题。也是本节最重要的一点。

一般可调用对象传引用的方式为:
普通回调:函数的形参为引用或者指针。(实际上detach时形参为引用也会拷贝,这个详细看下一章)
类内重载括号:类对象的内部数据成员声明为引用,然后子线程通过回调指针指向该对象地址的方式访问(这是类对象重载括号能作回调函数的根本原因),但是不建议使类内的数据成员声明为引用。下面的代码例子是使用这种方法讲解而已。
lambda表达式:与普通回调一样。

1)类对象重载括号作可调用对象传参,并且类对象成员存在引用(一般声明类很少类成员声明为引用)。
首先先抛出两个问题:
1)当主线程结束了,detach的子线程在括号()重载访问m_i会怎么样?
2)当主线程结束了,detach的子线程能再访问到ta对象吗,ta对象不是被释放了吗?

#include<iostream>
#include<thread>

using namespace std;

//类中包含operator(),此类的对象为可调用对象
class Ta{
public:

	Ta(int &i) : m_i(i){
		cout << "Ta()构造函数被执行了!" << endl;
	};

	Ta(const Ta &ta) : m_i(ta.m_i){
		cout << "Ta()复制构造函数被执行了!" << endl;
	}

	~Ta(){
		cout << "~Ta()析构函数被执行了!" << endl;
	}

	void operator()(){
		//打印多条是为了让主线程先执行完毕,让m_i引用消失
		cout << "我的线程operator()开始执行了!" << endl;
		cout << "我的线程operator()执行结束了!" << endl;
		cout << "我的线程operator()执行结束了!" << endl;
		cout << "我的线程operator()执行结束了!" << endl;
		cout << "我的线程operator()执行结束了!" << endl;
		cout << "我的线程operator()执行结束了!" << endl;
		cout << "我的线程operator()执行结束了!" << endl;
		cout << "我的线程operator()执行结束了!" << endl;
		cout << "我的线程operator()执行结束了!" << endl;
		cout << "m_i的值为:" << m_i << endl;
	}

private:
	int &m_i;
};

int main()
{
	int myi = 6;
	Ta ta(myi);

	thread myobj(ta);//ta为可调用对象
	//myobj.join(); //等待子线程执行结束

	//此时,如果主线程执行完了,myi就被释放了;子线程还没执行完,就会出现不可预料的bug;
	myobj.detach();  

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

	return 0;
}

看结果分析:
在这里插入图片描述

  • 1)先解释第一个问题:看上面结果知道,当主线程先结束,detach的子线程再通过括号访问m_i引用时,会出现访问错误,原因是m_i变量引用已经被释放了,所以多次访问会出现不同的值。

  • 2)第二个问题:上面其实已经说过,thread(ta)创建线程后再detach,ta在子线程为拷贝值,所以即使主线程结束了也不会出现问题。记住是detach时才会出现即使对象传引用也会拷贝值,但内部数据成员有引用时即m_i,该m_i数据是不会创建内存而直接引用,所以上面会报错,这一点很重要。总结第二点来说:detach时,即使传引用对象它也会进行拷贝,但若对象内部存在引用数据成员,该内部数据成员不会拷贝。
    例如:

//整个对象除了m_i不拷贝,其余内容都会拷贝
class A{
	int &m_i;//不会拷贝
	int m_j;//会拷贝
	char m_k;//会拷贝
};

总结:

  • 所以只要这个Ta类对象没有引用,没有指针,那么就不会产生问题。并且建议大家能使用join就使用join,使用join就不会出现上面的问题。

3)测试detach时对象会进行拷贝:
看上面的结果图,可以看到Ta()复制构造函数被执行了,所以detach就是会调用拷贝赋值。

5 可调用对象传lambda表达式
一般lambda表达式都是执行比较小的函数,所以是非常简单的,只要大概会使用即可。

#include<iostream>
#include<thread>

using namespace std;

int main(){

	auto myLambda = []() {
		cout << "子线程执行开始!" << endl;
		cout << "子线程执行完毕!" << endl;
	};

	thread myobj(myLambda);
	if (myobj.joinable() == true) {
		myobj.join();
		//myobj.detach();//不建议使用,主线程执行完毕的话子线程容易出现访问错误
	}

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

	return 0;
}

上面程序结果,使用join程序输出非常稳定。
在这里插入图片描述

6 总结本节
总而言之,能不用detach就别用,这样可以使我们不用考虑detach传参的大坑问题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

01C++11多线程编程之thread,join,detach,joinable以及简说detach传引用地址的大坑 的相关文章

  • C 编程 - 文件 - fwrite

    我有一个关于编程和文件的问题 while current NULL if current gt Id Doctor 0 current current gt next id doc current gt Id Doctor if curre
  • “构建”构建我的项目,“构建解决方案”则不构建

    我刚刚开始使用VS2010 我有一个较大的解决方案 已从 VS2008 成功迁移 我已将一个名为 Test 的控制台应用程序项目添加到解决方案中 选择构建 gt 构建解决方案不编译新项目 选择构建 gt 构建测试确实构建了项目 在失败的情况
  • GLKit的GLKMatrix“列专业”如何?

    前提A 当谈论线性存储器中的 列主 矩阵时 列被一个接一个地指定 使得存储器中的前 4 个条目对应于矩阵中的第一列 另一方面 行主 矩阵被理解为依次指定行 以便内存中的前 4 个条目指定矩阵的第一行 A GLKMatrix4看起来像这样 u
  • Web 客户端和 Expect100Continue

    使用 WebClient C NET 时设置 Expect100Continue 的最佳方法是什么 我有下面的代码 我仍然在标题中看到 100 continue 愚蠢的 apache 仍然抱怨 505 错误 string url http
  • 按成员序列化

    我已经实现了template
  • 在结构中使用 typedef 枚举并避免类型混合警告

    我正在使用 C99 我的编译器是 IAR Embedded workbench 但我认为这个问题对于其他一些编译器也有效 我有一个 typedef 枚举 其中包含一些项目 并且我向该新类型的结构添加了一个元素 typedef enum fo
  • 如何从 appsettings.json 文件中的对象数组读取值

    我的 appsettings json 文件 StudentBirthdays Anne 01 11 2000 Peter 29 07 2001 Jane 15 10 2001 John Not Mentioned 我有一个单独的配置类 p
  • 在 ASP.NET 5 中使用 DI 调用构造函数时解决依赖关系

    Web 上似乎充斥着如何在 ASP NET 5 中使用 DI 的示例 但没有一个示例显示如何调用构造函数并解决依赖关系 以下只是众多案例之一 http social technet microsoft com wiki contents a
  • C# 中通过 Process.Kill() 终止的进程的退出代码

    如果在我的 C 应用程序中 我正在创建一个可以正常终止或开始行为异常的子进程 在这种情况下 我通过调用 Process Kill 来终止它 但是 我想知道该进程是否已退出通常情况下 我知道我可以获得终止进程的错误代码 但是正常的退出代码是什
  • 重载<<的返回值

    include
  • 使用 Bearer Token 访问 IdentityServer4 上受保护的 API

    我试图寻找此问题的解决方案 但尚未找到正确的搜索文本 我的问题是 如何配置我的 IdentityServer 以便它也可以接受 授权带有 BearerTokens 的 Api 请求 我已经配置并运行了 IdentityServer4 我还在
  • SolrNet连接说明

    为什么 SolrNet 连接的容器保持静态 这是一个非常大的错误 因为当我们在应用程序中向应用程序发送异步请求时 SolrNet 会表现异常 在 SolrNet 中如何避免这个问题 class P static void M string
  • 转发声明和包含

    在使用库时 无论是我自己的还是外部的 都有很多带有前向声明的类 根据情况 相同的类也包含在内 当我使用某个类时 我需要知道该类使用的某些对象是前向声明的还是 include d 原因是我想知道是否应该包含两个标题还是只包含一个标题 现在我知
  • 如何序列化/反序列化自定义数据集

    我有一个 winforms 应用程序 它使用强类型的自定义数据集来保存数据进行处理 它由数据库中的数据填充 我有一个用户控件 它接受任何自定义数据集并在数据网格中显示内容 这用于测试和调试 为了使控件可重用 我将自定义数据集视为普通的 Sy
  • 如何查看网络连接状态是否发生变化?

    我正在编写一个应用程序 用于检查计算机是否连接到某个特定网络 并为我们的用户带来一些魔力 该应用程序将在后台运行并执行检查是否用户请求 托盘中的菜单 我还希望应用程序能够自动检查用户是否从有线更改为无线 或者断开连接并连接到新网络 并执行魔
  • 这些作业之间是否存在顺序点?

    以下代码中的两个赋值之间是否存在序列点 f f x 1 1 x 2 不 没有 在这种情况下 标准确实是含糊不清的 如果你想确认这一点 gcc 有这个非常酷的选项 Wsequence point在这种情况下 它会警告您该操作可能未定义
  • 将控制台重定向到 .NET 程序中的字符串

    如何重定向写入控制台的任何内容以写入字符串 对于您自己的流程 Console SetOut http msdn microsoft com en us library system console setout aspx并将其重定向到构建在
  • C# 模拟VolumeMute按下

    我得到以下代码来模拟音量静音按键 DllImport coredll dll SetLastError true static extern void keybd event byte bVk byte bScan int dwFlags
  • IEnumreable 动态和 lambda

    我想在 a 上使用 lambda 表达式IEnumerable
  • Windows 和 Linux 上的线程

    我在互联网上看到过在 Windows 上使用 C 制作多线程应用程序的教程 以及在 Linux 上执行相同操作的其他教程 但不能同时用于两者 是否存在即使在 Linux 或 Windows 上编译也能工作的函数 您需要使用一个包含两者的实现

随机推荐

  • 【Python】字典内容写入json文件

    Python中有序字典和无序字典 一键多值字典 Python将字典内容写入json文件 1 无序字典 目前了解三种 在Python中直接默认的是无序字典 这种不会按照你插入的顺序排序 即使你对字典排序后 返回的也是一个list变量 而不是字
  • echarts的图表双击事件、单击某块、滚轮放大缩小

    echarts的图表双击事件 dblclick
  • 华为机试:HJ2 计算某字符出现次数

    描述 写出一个程序 接受一个由字母 数字和空格组成的字符串 和一个字符 然后输出输入字符串中该字符的出现次数 不区分大小写字母 数据范围 1 le n le 1000 1 n 1000 输入描述 第一行输入一个由字母和数字以及空格组成的字符
  • Android 源码分析 - 系统 - Settings

    core 源代码 core java android provider Settings java 定义一系列配置项名称 索引 常量 SettingsProvider apk 源代码位于 frameworks base packages S
  • 【Vuex入门】——(四)Mutation

    一 Mutation的作用 更改 Vuex 的store 中的状态的唯一方法是提交 mutation Vuex 中的 mutation 非常类似于事件 每个 mutation 都有一个字符串的 事件类型 type 和 一个 回调函数 han
  • Python语言程序设计 习题6

    一 选择题 1 下列Python数据中其元素可以改变的是 A A 列表 B 元组 C 字符串 D 数组 2 表达式 2 in 1 2 3 4 的值是 D A Yes B No C True D False print 2 in 1 2 3
  • python是什么意思中文、好学吗-零基础学python难吗?好学吗?

    Python是一种什么语言 Python是一种计算机程序设计语言 你可能已经听说过很多种流行的编程语言 比如非常难学的C语言 非常流行的Java语言 适合初学者的Basic语言 适合网页编程的JavaScript语言等 Python是他们其
  • 如何使用下标遍历二维数组

    点击打开链接 int my 2d array 10 10 假定数组my 2d array 已经预先被填充了数据 int i j 遍历这个数组 for i 0 i lt 10 i 向下遍历各行 for j 0 j lt 10 j 穿越各列 p
  • Python:Unused import statement 解决方法

    Python 学习 21052501 1 Unused import statement 解决方法 Pycharm file 菜单下有Invalidate caches Restart菜单栏 点击清除缓存重新启动Pycharm即可 2 In
  • 反思深度学习与传统计算机视觉的关系

    来源 算法与数学之美 某种程度上 深度学习最大的优势就是自动创建没有人会想到的特性能力 如今 深度学习在众多领域都有一席之地 尤其是在计算机视觉领域 尽管许多人都为之深深着迷 然而 深网就相当于一个黑盒子 我们大多数人 甚至是该领域接受过培
  • 【C++进阶】二叉搜索树递归与非递归的模拟实现(附源码)

    一 什么是二叉搜索树 二叉搜索树又称二叉排序树 它或者是一棵空树 或者是具有以下性质的二叉树 根据二叉搜索树的性质 它的中序遍历结果就是一个升序列 二 二叉搜索树的模拟实现 节点 Node 在实现二叉搜索树之前 要先定义一个节点 成员变量包
  • Shiro权限框架-在线并发登录人数控制(9)

    1 实现原理 在实际开发中 我们可能会遇到这样的需求 一个账号只允许同时一个在线 当账号在其他地方登陆的时候 会踢出前面登陆的账号 那我们怎么实现 自定义过滤器 继承AccessControlFilter 使用redis队列控制账号在线数目
  • 基于Directshow的H.264流媒体播放器设计

    0引言 DirectsHow应用框架完成了流媒体处理的底层工作 使得编程者无需关心数据如何输入 以及处理完后如何输出 而只需关心如何对输入数据进行处理 H 264视频编解码标准具有高压缩比和优良的网络亲和性 被普遍认为是最有影响力的流媒体视
  • 变分推断

    变分推断 MATLAB实现变分贝叶斯蒙特卡洛模拟的贝叶斯推断 目录 变分推断 MATLAB实现变分贝叶斯蒙特卡洛模拟的贝叶斯推断 效果一览 基本介绍 研究内容 模型描述 模型设计 参考资料 效果一览 基本介绍 MATLAB实现变分贝叶斯蒙特
  • input输入框获取焦点之后,显示搜索记录下拉表,点击其他地方搜索记录框消失

    给input框绑定一个focus事件 获取焦点时给全局绑定一个点击事件 判断下次点击的地方在不在输入框和下拉框内 不在则下拉框消失 记得清除这个点击事件 具体代码如下 处理搜索框获取焦点 handleInputSearch this isO
  • elementui中多个table同步滚动

    问题描述 element admin中同时使用多个table 要求头部固定 给每个并列的table设置max height 通过监听一侧的table滚动情况去控制另外一侧的table滚动 问题分析 table分成两部分 左侧是一个table
  • 用python画基本初等函数的图像(未完成)

    要用到matplotlib库 一 绘制 y x 图像 import numpy as np import matplotlib pyplot as plt x np linspace 50 50 200 定义x的范围为 50到50 分为20
  • UVA-11059 最大乘积 题解答案代码 算法竞赛入门经典第二版

    GitHub jzplp aoapc UVA Answer 算法竞赛入门经典 例题和习题答案 刘汝佳 第二版 数据量不大 暴力即可 include
  • 【AI】《动手学-深度学习-PyTorch版》笔记(二十):图像增强、微调

    AI学习目录汇总 1 图像增强 图像增强可以扩展训练样本数量 减小对某个属性的依赖 比如 裁剪图像 可以减少模型对对象出现位置的依赖 调整亮度 颜色等因素来降低模型对颜色的敏感度等 1 1 准备工作 头文件 matplotlib inlin
  • 01C++11多线程编程之thread,join,detach,joinable以及简说detach传引用地址的大坑

    01C 11多线程编程之thread join detach joinable以及简说detach传引用地址的大坑 1 thread类对象创建线程与join回收线程 1 thread创建线程很简单 定义一个对象然后传一个可调用对象即可 可调