zoukankan      html  css  js  c++  java
  • 【C++多线程】传递参数

      线程可以共享进程的内存空间,线程拥有自己独立内存。

      关于参数的传递,std::thread的构造函数只会单纯的复制传入的变量,特别需要注意的是传递引用时,传入的是值的副本,也就是说子线程中的修改影响不了主线程中的值。

    值传递

      主线程中的值,被拷贝一份传到了子线程中。

     1 #include <iostream>
     2 #include <thread>
     3 
     4 using namespace std;
     5 
     6 void test(int ti, int tj)
     7 {
     8     cout << "子线程开始" << endl;
     9     //ti的内存地址0x0055f69c {4},tj的内存地址0x0055f6a0 {5}
    10     cout << ti << " " << tj << endl;  
    11     cout << "子线程结束" << endl;
    12     return;
    13 }
    14 
    15 
    16 int main()
    17 {
    18     cout << "主线程开始" << endl;
    19     //i的内存地址0x001efdfc {4},j的内存地址0x001efdf0 {5}
    20     int i = 4, j = 5;  
    21     thread t(test, i, j);
    22     t.join();
    23     cout << "主线程结束!" << endl;
    24     return 0;
    25 }

    传引用

      从下面的运行结果,可以看出,即使是用引用来接收传的值,也是会将其拷贝一份到子线程的独立内存中,这一点与我们编写普通程序时不同。这是因为线程的创建属于函数式编程,所以为了传引用C++中才引入了std::ref()。关于std::ref()

     1 #include <iostream>
     2 #include <thread>
     3 
     4 using namespace std;
     5 
     6 class A{
     7 public:
     8      int ai;
     9      A (int i): ai(i) { }
    10 };
    11 
    12 //这种情况必须在引用前加const,否则会出错。目前本人的觉得可能是因为临时对象具有常性
    13 void test(const int &ti, const A &t) 
    14 {
    15     cout << "子线程开始" << endl;
    16     //ti的内存地址0x0126d2ec {4},t.ai的内存地址0x0126d2e8 {ai=5 }
    17     cout << ti << " " << t.ai << endl;  
    18     cout << "子线程结束" << endl;
    19     return;
    20 }
    21 
    22 
    23 int main()
    24 {
    25     cout << "主线程开始" << endl;
    26     //i的内存地址0x010ff834 {4},a的内存地址0x010ff828 {ai=5 }
    27     int i = 4;  
    28     A a = A(5);
    29     thread t(test, i, a);
    30     t.join();
    31     cout << "主线程结束!" << endl;
    32     return 0;
    33 }

       那么如果我们真的需要像一般程序那样传递引用呢,即在子线程中的修改能够反映到主线程中。此时需要使用std::ref()但是注意如果我们会在子线中改变它,此时用于接收ref()的那个参数前不能加const。关于C++多线程中的参数传引用问题,我目前只是记住了这个现象,关于原理还需后期研究源码继续学习。

     1 #include <iostream>
     2 #include <thread>
     3 
     4 using namespace std;
     5 
     6 class A {
     7 public:
     8     int ai;
     9     A(int i) : ai(i) { }
    10 };
    11 
    12 //接收ref()的那个参数前不能加const,因为我们会改变那个值
    13 void test(int& ti, const A& t)
    14 {
    15     cout << "子线程开始" << endl;
    16     cout << ti << " " << t.ai << endl;
    17     ti++;
    18     cout << "子线程结束" << endl;
    19     return;
    20 }
    21 
    22 
    23 int main()
    24 {
    25     cout << "主线程开始" << endl;
    26     int i = 4;
    27     A a = A(5);
    28     thread t(test, ref(i), a);
    29     t.join();
    30     cout << "i改变:" << i << endl;
    31     cout << "主线程结束!" << endl;
    32     return 0;
    33 }

       传入类对象时,使用引用来接收比用值接收更高效。

     1 #include <iostream>
     2 #include <thread>
     3 
     4 using namespace std;
     5 
     6 class A {
     7 public:
     8     int ai;
     9     A (int i) : ai(i) 
    10     { 
    11         cout << "构造" << this << endl; 
    12     }
    13 
    14     A (const A& a) :ai(a.ai) {
    15         cout << "拷贝构造" << this << endl;
    16     }
    17 
    18     ~A()
    19     {
    20         cout << "析构" << this << endl;
    21     }
    22 };
    23 
    24 //void test(const A a)
    25 void test(const A& a)
    26 {
    27     cout << "子线程开始" << endl;
    28     cout << "子线程结束" << endl;
    29     return;
    30 }
    31 
    32 
    33 int main()
    34 {
    35     cout << "主线程开始" << endl;
    36     int i = 4;
    37     thread t(test, A(i));
    38     t.join();
    39     cout << "主线程结束!" << endl;
    40     return 0;
    41 }

     

    传指针

      从下面的运行结果,可以看出,主线程和子线程中的指针都是指向同一块内存。所以在这种情况下会有一个陷阱,如果使用detach(),则当主线程崩溃或者正常结束后,该块内存被回收,若此时子线程没有结束,那么子线程中指针的访问将未定义,程序会出错。

     1 #include <iostream>
     2 #include <thread>
     3 
     4 using namespace std;
     5 
     6 
     7 void test(char *p) 
     8 {
     9     cout << "子线程开始" << endl;
    10     //0x004ffeb4 "hello"
    11     cout << p << endl;  
    12     cout << "子线程结束" << endl;
    13     return;
    14 }
    15 
    16 
    17 int main()
    18 {
    19     cout << "主线程开始" << endl;
    20     //0x004ffeb4 "hello"
    21     char s[] = "hello";
    22     thread t(test, s);
    23     t.join();
    24     cout << "主线程结束!" << endl;
    25     return 0;
    26 }

    传临时对象

       用临时变量作为实参时,会更高效,由于临时变量会隐式自动进行移动操作,这就减少了整体构造函数的调用次数。而一个命名变量的移动操作就需要std::move()。

     1 #include <iostream>
     2 #include <thread>
     3 
     4 using namespace std;
     5 
     6 class A {
     7 public:
     8     int ai;
     9     A (int i) : ai(i) 
    10     { 
    11         cout << "构造" << this << endl; 
    12     }
    13 
    14     A (const A& a) :ai(a.ai) {
    15         cout << "拷贝构造" << this << endl;
    16     }
    17 
    18     ~A()
    19     {
    20         cout << "析构" << this << endl;
    21     }
    22 };
    23 
    24 void test(const A& a)
    25 {
    26     cout << "子线程开始" << endl;
    27     cout << "子线程结束" << endl;
    28     return;
    29 }
    30 
    31 
    32 int main()
    33 {
    34     cout << "主线程开始" << endl;
    35     int i = 4;
    36     thread t(test, A(i));
    37     t.join();
    38     cout << "主线程结束!" << endl;
    39     return 0;
    40 }

    总结

      1、使用引用和指针是要注意;

      2、对于内置简单类型,建议传值;

      3、对于类对象,建议使用引用来接收,以为使用引用会只会构造两次,而传值会构造三次;

      4、在detach下要避免隐式转换,因为此时子线程可能还来不及转换主线程就结束了,应该在构造线程时,用参数构造一个临时对象传入。

  • 相关阅读:
    Advanced Configuration Tricks
    Reviewing the Blog Module
    Editing and Deleting Data
    Making Use of Forms and Fieldsets
    Understanding the Router
    SQL Abstraction and Object Hydration
    Preparing for Different Databases
    Java学习理解路线图
    Openstack学习历程_1_视频
    CentOS安装Nginx负载
  • 原文地址:https://www.cnblogs.com/chen-cs/p/13056703.html
Copyright © 2011-2022 走看看