一、detach()大坑

上一篇随笔(二)中提到detach()是用来分离主线程和子线程的,那么需要考虑一个问题,就是如果主线程跑完了,主线程中定义的变量就会被销毁(释放内存),这时回收变量仍作为参数传入子线程,那么就会出现问题,下面用一个例子详细说明。

 #include <iostream>
#include <string>
#include <thread>
using namespace std; void MyThread(const int& a, char* str)
{
cout << a << endl;
cout << str << endl;
}
int main()
{ int n = ;
char str_m[] = "hello wrold";
thread MyThreadObj(MyThread, n, str_m);
if (MyThreadObj.joinable())
{
MyThreadObj.detach();
}
cout << "main_thread" << endl;
system("pause");
return ;
}

由监视图可知,实参n和形参a的地址并不同,所以实际是值传递,并因此最好不要用引用,直接用值传递就行了。

主线程的str_m和str的地址却相同,那么当子线程和主线程分离时,就会出现问题。这里讲一个改进的方法,把形参char* str改成const string& str,即把传进来的参数隐式地转换成一个(构造了)string对象,会发现主线程的str_m和子线程形参str的地址是不同的,但实际上问题还是存在,如果在在传递主线程的参数str_m前,str_m就被回收了,一个被回收的变量作为参数结果可想而知。所以还要再改多一步,就是在main()中实参str_m改成string(str_m),先构造一个string对象,再将这个对象作为参数传入子线程,这时又会有疑问,难道不会出现在string(str_m)前str_m就被系统回收了吗?答案是不会的,测试方法这里不细说了。

下面是改进后的代码

 #include <iostream>
#include <string>
#include <thread>
using namespace std; void MyThread(int a, const string& str)
{
cout << a << endl;
cout << str << endl;
}
int main()
{ int n = ;
char str_m[] = "hello wrold";
thread MyThreadObj(MyThread, n, string(str_m));
if (MyThreadObj.joinable())
{
MyThreadObj.detach();
}
cout << "main_thread" << endl;
system("pause");
return ;
}

 二、std::this_thread::get_id()

线程id,每个线程都有自己的线程id,各个id都不同,获取线程id方法为std::this_thread::get_id()

 #include <iostream>
#include <string>
#include <thread>
using namespace std;
class CA
{
public:
int a;
CA(int m) :a(m)
{
cout << "构造函数执行 " << endl;
}
CA(const CA&m) :a(m.a)
{
cout << "拷贝构造函数执行 " << endl;
}
};
void MyThread(const CA&cc)
{
cout << "子线程id:" << this_thread::get_id() << endl;
cout << cc.a << endl;
}
int main()
{
cout << "主线程id:" << this_thread::get_id() << endl;
CA ca();
thread mythreadObj(MyThread,ca);
mythreadObj.join();
system("pause");
return ;
}

三、std::ref()

上述代码运行可知,如果向子线程传入一个对象,会调用拷贝构造函数,这时用detach()是安全的,但是如果想要通过子线程来修改主线程对象的数据是不允许的,可以把18行的const去掉试试,是会报错的,那么可以用std::ref()来使这个对象得以修改。(当然,18行处也可以改成void MyThread(CA cc),这样也是可以修改,但是修改的只是拷贝的对象,对主线程的对象没有影响,而且会调用两次拷贝构造函数,浪费内存)

 #include <iostream>
#include <string>
#include <thread>
using namespace std;
class CA
{
public:
int a;
CA(int m) :a(m)
{
cout << "构造函数执行 " << endl;
}
CA(const CA&m) :a(m.a)
{
cout << "拷贝构造函数执行 " << endl;
}
};
void MyThread(CA& cc)
{
cc.a++;
cout << "子线程id:" << this_thread::get_id() << endl;
cout << "my_thread " <<"ca.a:" << cc.a << endl;
}
int main()
{
cout << "主线程id:" << this_thread::get_id() << endl;
CA ca();
thread mythreadObj(MyThread, ref(ca));
cout << "main_thread " << "ca.a:" << ca.a << endl;
mythreadObj.join();
system("pause");
return ;
}

由结果可知,并不会调用拷贝构造函数,在子线程中操作的是主线程中的对象。这时需要注意了,如果用detach(),就不安全了。还是那个问题,对已经释放的变量进行非法操作必定带来隐患。

C++多线程基础学习笔记(三)的更多相关文章

  1. Java基础学习笔记三 Java基础语法

    Scanner类 Scanner类属于引用数据类型,先了解下引用数据类型. 引用数据类型的使用 与定义基本数据类型变量不同,引用数据类型的变量定义及赋值有一个相对固定的步骤或格式. 数据类型 变量名 ...

  2. C++多线程基础学习笔记(四)

    一.创建多个子线程 前面三章讲的例子都是只有一个子线程和主线程,然而实际中有多个子线程.那么下面介绍如何创建多个子线程. #include <iostream> #include < ...

  3. C++多线程基础学习笔记(一)

    下面分三个方面多线程技术的必须掌握一些基本知识. 1.进程 2.线程 3.并发 (1)进程 一个可执行程序运行起来了,即为创建了一个进程.如在电脑上打开了word,就创建了一个word进程,打开QQ, ...

  4. Java基础学习笔记三 正则表达式和校验、Date、DateFormat、Calendar

    正则表达式 正则表达式(英语:Regular Expression,在代码中常简写为regex).正则表达式是一个字符串,使用单个字符串来描述.用来定义匹配规则,匹配一系列符合某个句法规则的字符串.在 ...

  5. C++多线程基础学习笔记(七)

    一.std::async和std::future的用法 std::async是一个函数模板,std::future是一个类模板 #include <iostream> #include & ...

  6. C++多线程基础学习笔记(六)

    condition_variable.wait.notifiy_one.notify_all的使用方式 condition_variable:条件变量 wait:等待被唤醒 notify_one:随机 ...

  7. loadrunner基础学习笔记三

    运行时设置: 打开运行时设置:任务窗格中-选择回放-点击运行时设置按钮  1 重复执行次数:=2 2 步:控制迭代时间间隔 3 日志设置:指出要在运行测试期间记录的信息量 4 思考时间:可以在cont ...

  8. JSP实现数据传递(web基础学习笔记三)

    get和post的区别: JSP内置对象: 1)out内置对象:out内置对象是在JSP开发过程中使用得最为频繁的对象,然而其使用起来也是最简单的.out对象用于向客户端浏览器输出数         ...

  9. Java基础学习笔记(三) - 抽象类和接口

    一.抽象类 没有方法主体的方法称为抽象方法,包含抽象方法的类就是抽象类. Java中使用 abstract 关键字修饰方法和类,抽象方法只有一个方法名,没有方法体. public abstract c ...

随机推荐

  1. python 多线程实现循环打印 abc

    python 多线程实现循环打印 abc 好久没写过python了, 想自己实践一下把 非阻塞版 import threading import time def print_a(): global ...

  2. 2019牛客暑期多校训练营(第二场)E 线段树维护dp转移矩阵

    题意 给一个\(n\times m\)的01矩阵,1代表有墙,否则没有,每一步可以从\(b[i][j]\)走到\(b[i+1][j]\),\(b[i][j-1]\),\(b[i][j+1]\),有两种 ...

  3. Python基础之变量和常量

    变量 将运算得到中间结果暂存到内存,以便后续程序调用. 变量的命名规则: 变量由字母.数字.下划线搭配组合而成 不能以数字开头,更不能全是数字 不能是Python中的关键字,这些符号和字母已经被Pyt ...

  4. docker之CPU配额参数的混合使用

    在启动容器的时候有很多参数,这里来实践一下与CPU相关的参数. 实例: 创建两个容器,docker10.docker20,让两个容器只运行在CPU0上,然后测试CPU使用率. [root@openst ...

  5. sql注入的基本小知识

    load_fie('') into outfile '' into dumpfile('') 堆叠注入 ;insert into liunx密码读取 /etc/passwd /etc/shadow W ...

  6. JavaScript数字计算精度丢失的问题和解决方案

    一.JS数字精度丢失的一些典型问题 1. 两个简单的浮点数相加:0.1 + 0.2 != 0.3 // true,下图是firebug的控制台截图: 看看java的计算结果:是不是让你很不能接受 再来 ...

  7. pandas.Series.value_counts

    pandas.Series.value_counts Series.value_counts(normalize=False, sort=True, ascending=False, bins=Non ...

  8. python 面对对象 类(继承, 多态)

    继承,继承其它实例化样本的属性和方法,需要在声明里重新定义和使用 class School(object): def __init__(self, name, addr): self.name = n ...

  9. FPGA实战操作(2) -- PCIe总线(例程设计分析)

    1.框架总览 平台:vivado 2016.4 FPGA:A7 在实际应用中,我们几乎不可能自己去编写接口协议,所以在IP核的例程上进行修改来适用于项目是个不错的选择. 通过vivado 中有关PCI ...

  10. flutter 打包

    iOS打包 iOS打包需要注意一下一些设置 info.plist 设置ATS.白名单.字符串等等 Assets.xcassets 替换icon,替换LaunchImage中内容 注意LaunchIma ...