系列文章目录
文章目录
- 系列文章目录
- -进程
- -前言
- 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 <iostream>
#include <thread>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);
};
线程执行函数
- 普通函数
void thdjob(int n) {std::cout << "子线程执行:" << std::this_thread::get_id() << std::endl; }
- 类的成员函数
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引用,并且会发生
- 无对象拷贝的方式:
- 引用
void thdjob(Base& b);// 子线程中的对象b与主线程中的是同一个,自然无对象拷贝// 需要确保子线程在使用b时,主线程不会将其销毁std::thread th(thdjob, std::ref(b)); // std::ref将b变为引用类型
- 指针
void thdjob(Base* b);// 用地址传递自然都是同一个对象std::thread th(thdjob, &b);
- 引用
- 发生对象拷贝
- 2次拷贝,主线程子线程各一次
发生两次对象拷贝,第一次发生在主线程,将b对象拷贝到th,第二次发生在子线程,将th中的右值对象拷贝到形参void thdjob(Base b); std::thread th(thdjob, b);
默认构造函数Base()运行 this: 0x7ffcd8b7f014 id= 139770366710720 ---------- 拷贝构造函数Base(&) this: 0x558e2431d2c8 id= 139770366710720 拷贝构造函数Base(&) this: 0x7f1ed29fed74 id= 139770359445056 this: 0x7f1ed29fed74子线程执行:139770359445056
- 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);
解析:主线程创建b的拷贝,即使主线程结束也是安全的;子线程引用b的拷贝,当子线程结束时负责析构该对象。默认构造函数Base()运行 this: 0x7ffd26fb3764 id= 140183106712512 ---------- 拷贝构造函数Base(&) this: 0x563e24eee2c8 id= 140183106712512 this: 0x563e24eee2c8子线程执行:140183099930176
- 2次拷贝,主线程子线程各一次
-c++11线程对象创建后既不join()也不detach()的后果
c++11中,创建对象(std::thread)后有两种状态:
joinablenonjoinable
线程对象通过默认构造函数构造后状态为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