一、detach()大坑

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

  1. #include <iostream>
  2. #include <string>
  3. #include <thread>
  4. using namespace std;
  5.  
  6. void MyThread(const int& a, char* str)
  7. {
  8. cout << a << endl;
  9. cout << str << endl;
  10. }
  11. int main()
  12. {
  13.  
  14. int n = ;
  15. char str_m[] = "hello wrold";
  16. thread MyThreadObj(MyThread, n, str_m);
  17. if (MyThreadObj.joinable())
  18. {
  19. MyThreadObj.detach();
  20. }
  21. cout << "main_thread" << endl;
  22. system("pause");
  23. return ;
  24. }

由监视图可知,实参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就被系统回收了吗?答案是不会的,测试方法这里不细说了。

下面是改进后的代码

  1. #include <iostream>
  2. #include <string>
  3. #include <thread>
  4. using namespace std;
  5.  
  6. void MyThread(int a, const string& str)
  7. {
  8. cout << a << endl;
  9. cout << str << endl;
  10. }
  11. int main()
  12. {
  13.  
  14. int n = ;
  15. char str_m[] = "hello wrold";
  16. thread MyThreadObj(MyThread, n, string(str_m));
  17. if (MyThreadObj.joinable())
  18. {
  19. MyThreadObj.detach();
  20. }
  21. cout << "main_thread" << endl;
  22. system("pause");
  23. return ;
  24. }

 二、std::this_thread::get_id()

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

  1. #include <iostream>
  2. #include <string>
  3. #include <thread>
  4. using namespace std;
  5. class CA
  6. {
  7. public:
  8. int a;
  9. CA(int m) :a(m)
  10. {
  11. cout << "构造函数执行 " << endl;
  12. }
  13. CA(const CA&m) :a(m.a)
  14. {
  15. cout << "拷贝构造函数执行 " << endl;
  16. }
  17. };
  18. void MyThread(const CA&cc)
  19. {
  20. cout << "子线程id:" << this_thread::get_id() << endl;
  21. cout << cc.a << endl;
  22. }
  23. int main()
  24. {
  25. cout << "主线程id:" << this_thread::get_id() << endl;
  26. CA ca();
  27. thread mythreadObj(MyThread,ca);
  28. mythreadObj.join();
  29. system("pause");
  30. return ;
  31. }

三、std::ref()

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

  1. #include <iostream>
  2. #include <string>
  3. #include <thread>
  4. using namespace std;
  5. class CA
  6. {
  7. public:
  8. int a;
  9. CA(int m) :a(m)
  10. {
  11. cout << "构造函数执行 " << endl;
  12. }
  13. CA(const CA&m) :a(m.a)
  14. {
  15. cout << "拷贝构造函数执行 " << endl;
  16. }
  17. };
  18. void MyThread(CA& cc)
  19. {
  20. cc.a++;
  21. cout << "子线程id:" << this_thread::get_id() << endl;
  22. cout << "my_thread " <<"ca.a:" << cc.a << endl;
  23. }
  24. int main()
  25. {
  26. cout << "主线程id:" << this_thread::get_id() << endl;
  27. CA ca();
  28. thread mythreadObj(MyThread, ref(ca));
  29. cout << "main_thread " << "ca.a:" << ca.a << endl;
  30. mythreadObj.join();
  31. system("pause");
  32. return ;
  33. }

由结果可知,并不会调用拷贝构造函数,在子线程中操作的是主线程中的对象。这时需要注意了,如果用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. easyUI datagrid中checkbox选中事件以及行点击事件,翻页之后还可以选中

    DataGrid其中与选择,勾选相关 DataGrid属性:singleSelect boolean 如果为true,则只允许选择一行. false ctrlSelect boolean 在启用多行选 ...

  2. Java当中的IO流(上)

    Java当中的IO流 在Java中,字符串string可以用来操作文本数据内容,字符串缓冲区是什么呢?其实就是个容器,也是用来存储很多的数据类型的字符串,基本数据类型包装类的出现可以用来解决字符串和基 ...

  3. 创建虚拟机,安装操作系统,xshell6远程链接

    一.创建虚拟机 1. 首先安装vmware,注意在安装中,下面的两项不要勾选,一路下一步 2.完成安装打开之后,创建新的虚拟机 3.虚拟机创建完成,需要改配置 4.然后设置网段 5.查看服务,在运行状 ...

  4. 支持快应用的http网络库-flyio

    Fly.js 一个基于Promise的.强大的.支持多种JavaScript运行时的http请求库. 有了它,您可以使用一份http请求代码在浏览器.微信小程序.Weex.Node.React Nat ...

  5. Oracle实现分页,每页有多少条记录数

    分页一直都是关系数据库的热门,在数据量非常多的情况下,需要根据分页展示,每页展示多少条记录,以此减轻数据的压力; 1实现原理,根据rownum取记录数,根据公式(页数-1)*每页想要展示的记录数 AN ...

  6. Vue_(组件)实例属性

    Vue实例属性与方法中文文档 传送门   Vue实例属性:vue实例直接调用的属性 Learn 一.vm.$data:获取属性 二.vm.$el:获取实例挂载的元素 三.vm.$options:获取自 ...

  7. python3 selenium使用

    其实这个就相当于模拟人的点击事件来连续的访问浏览器.如果你玩过王者荣耀的话在2016年一月份的版本里面就有一个bug. 安卓手机下载一个按键精灵就可以在冒险模式里面设置按键,让手机自动玩闯关,一局19 ...

  8. [BZOJ2730]:[HNOI2012]矿场搭建(塔尖)

    题目传送门 题目描述 煤矿工地可以看成是由隧道连接挖煤点组成的无向图.为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处.于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个 ...

  9. (一)C语言的四大数据类型

  10. TCP->IP输出 之 ip_queue_xmit、ip_build_and_send_pkt、ip_send_unicast_reply

    概述 ip_queue_xmit是ip层提供给tcp层发送回调,大多数tcp发送都会使用这个回调,tcp层使用tcp_transmit_skb封装了tcp头之后,调用该函数,该函数提供了路由查找校验. ...