C++多线程基础学习笔记(三)

一、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 = 20;
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 0;
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 = 20;
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 0;
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(10);
27     thread mythreadObj(MyThread,ca);
28     mythreadObj.join();
29     system("pause");
30     return 0;
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(10);
28     thread mythreadObj(MyThread, ref(ca));
29     cout << "main_thread " << "ca.a:" << ca.a << endl;
30     mythreadObj.join();
31     system("pause");
32     return 0;
33 }

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

原文地址:https://www.cnblogs.com/main404/p/11161671.html