c++并发编程/多线程 thread 库

系列文章目录


文章目录

  • 系列文章目录
  • -进程
  • -前言
      • base类
      • 线程执行函数
      • 结果分析
      • 小结,行为总结
  • -c++11线程对象创建后既不join()也不detach()的后果
  • -
  • 附注代码


-进程

进程是运行着的程序

进程内存空间分配:略

如果主进程结束而子进程未结束,则Linux内核会将该子进程的父进程ID改为1(init进程),

-前言

void funcname(const A& v);

std::thread(funcname, value); // 即使函数的形参是引用类型也会发生拷贝构造,
除非:
void funcname(A& v);
std::thread(funcname, std::ref(value)); // 这样value对象就是主线程中的对象

base类

#include 
#include 

class Base
{
public:
    int num = 1;  // 类内初始化
    Base() 
    { 
        std::cout << "默认构造函数Base()运行 "
        << " this: " << this << " id= "
        << std::this_thread::get_id() << std::endl;
    }
    Base(const Base& b)
    {
        std::cout << "拷贝构造函数Base(&)"
        << " this: " << this << " id= "
        << std::this_thread::get_id() << std::endl;
    }
    ~Base()
    {
        std::cout << "析构函数~Base()"
        << " this: " << this << " id= "
        << std::this_thread::get_id() << std::endl;
    }
    void operator()(int num)
    {
        std::cout << "运算符重载,子线程执行: "
        << " this: " << this << " id= "
        << std::this_thread::get_id() << std::endl;
    }
    void thdjob(int n);
};

线程执行函数

  1. 普通函数
    void thdjob(int n)
    {
        std::cout << "子线程执行:" 
                  << std::this_thread::get_id() 
                  << std::endl;
    }
    
  2. 类的成员函数
    void Base::thdjob(int n)
     {
         std::cout << "子线程执行:" 
                   << std::this_thread::get_id() 
                   << std::endl;
     }
    

结果分析

int main()
{
    Base b;
    // 在第一个参数为普通函数的情况下,引用?
    // 当第一个参数为类的成员函数时,则子线程和主线程用的不是同一个对象
    // 若为引用或地址则为同一个对象
    std::thread thd(&Base::thdjob, b, 4);
}

小结,行为总结

  • std::thread中,即使线程函数的形参是引用类型也会进行对象拷贝
  • std::thread(…)中假定所有实参都为右值
    void thdjob(const Base& b); // 必须是const引用,并且会发生
    
  • 无对象拷贝的方式:
    1. 引用
       void thdjob(Base& b);
       // 子线程中的对象b与主线程中的是同一个,自然无对象拷贝
       // 需要确保子线程在使用b时,主线程不会将其销毁
       std::thread th(thdjob, std::ref(b));  // std::ref将b变为引用类型
      
    2. 指针
       void thdjob(Base* b);
       // 用地址传递自然都是同一个对象
       std::thread th(thdjob, &b);
      
  • 发生对象拷贝
    1.  2次拷贝,主线程子线程各一次
      void thdjob(Base b);
      std::thread th(thdjob, b);
      
      发生两次对象拷贝,第一次发生在主线程,将b对象拷贝到th,第二次发生在子线程,将th中的右值对象拷贝到形参
      默认构造函数Base()运行  this: 0x7ffcd8b7f014 id= 139770366710720
      ----------
      拷贝构造函数Base(&) this: 0x558e2431d2c8 id= 139770366710720
      拷贝构造函数Base(&) this: 0x7f1ed29fed74 id= 139770359445056
      this: 0x7f1ed29fed74子线程执行:139770359445056
      
    2.  1次拷贝
      // 子线程发生一次对象拷贝
      void thdjob(Base b);
      // std::ref  主线程无拷贝
      std::thread th(thdjob, std::ref(b));
      
      默认构造函数Base()运行  this: 0x7ffd5c67123c id= 139786634453952
      ----------
      拷贝构造函数Base(&) this: 0x7f229c3fed74 id= 139786627053120
      this: 0x7f229c3fed74子线程执行:139786627053120
      

      推荐的方式
      // 子线程无拷贝
      void thdjob(const Base& b);
      // 主线程进行1次对象拷贝
      std::thread th(thdjob, b);
      
      默认构造函数Base()运行  this: 0x7ffd26fb3764 id= 140183106712512
      ----------
      拷贝构造函数Base(&) this: 0x563e24eee2c8 id= 140183106712512
      this: 0x563e24eee2c8子线程执行:140183099930176
      
      解析:主线程创建b的拷贝,即使主线程结束也是安全的;子线程引用b的拷贝,当子线程结束时负责析构该对象。

-c++11线程对象创建后既不join()也不detach()的后果

c++11中,创建对象(std::thread)后有两种状态:

joinable

nonjoinable

线程对象通过默认构造函数构造后状态为nonjoinable; 线程对象通过有参构造函数创建后状态为join able。joinable状态的线程对象被调用join()或detach()会变为nonjoinable状态。

线程对象析构

// thread类中的析构函数定义:
~thread()
{
    if(nonjoinable){
        std::terminate();
    }
}

线程对象析构时,会判断线程的状态。如果线程处于join able状态时,会调用terminate()函数直接令程序退出。

也就是说,创建一个可运行(创建时传入线程函数)线程对象后,必须对该对象进行处理,要么调用join()要么调用detach(),否则线程对象析构时程序将直接退出。

-


附注代码

推荐的方式

void testfn(const Base & b)
{
    std::cout << "this = " << &b << " tid = " << std::this_thread::get_id()
              << std::endl;
    usleep(10000000);
    std::cout << b.num << std::endl;
}

void subth()
{
    Base B;
    std::cout << "subth Base B this =  " << &B 
              << " tid = " << std::this_thread::get_id() << std::endl;
    std::thread th(testfn, B);
    th.detach();
}

int main()
{
  std::cout << "main tid: " << std::this_thread::get_id() << std::endl;
  std::thread th(subth);
  th.detach();
  std::cout << "main sleep" << std::endl;
  while(1);
}
main tid: 140303695139776
main sleep
默认构造函数Base()运行  this: 0x7f9aff7fed6c id= 140303688267328
subth Base B this =  0x7f9aff7fed6c tid = 140303688267328
拷贝构造函数Base(&) this: 0x7f9af8000b78 id= 140303688267328
析构函数~Base() this: 0x7f9aff7fed6c id= 140303688267328
this = 0x7f9af8000b78 tid = 140303679874624
1
析构函数~Base() this: 0x7f9af8000b78 id= 140303679874624

你可能感兴趣的:(C++,标准库,c++)