C++11对多线程的支持
c++11中新支持了thread这个库,常见的创建线程、join()、detach()都能支持。所以我们能写出跨平台的多线程程序了。
进程和线程
在我们运行一个程序的时候,像我们写的一个cpp控制台程序运行起来,它就是一个进程。计算机运行可以有多个进程,进程里面也可以有多个线程,我们最熟悉的一个线程就是main()这个主线程了,主线程结束运行,这个进程也就结束了。
进程 是操作系统动态执行的基本单元,它可以被描述成一段程序的执行过程。进程之间资源不共享。
线程是操作系统工作的最小单元,创建线程可以有效提高效率,可以利用多核优势,对io密集型优化明显,程序可以在io的时候让另一个线程干其他事。线程之间资源共享
创建一个线程
接下来我们就用C++11提供的线程库thread创建一个线程。
#include <iostream>
#include <thread> //C++11线程库头文件
using namespace std;
void my_thread(int num){
cout << "you create a thread!" << num<< endl;
cout << "the thread id is:" << std::this_thread::get_id() << endl;
return;
}
int main(){
cout << "main's thread id is:" << std::this_thread::get_id() << endl;
thread thread_oj(my_thread,1); //第一个参数为线程入口函数,第二个参数为线程入口函数的参数。
thread_oj.join(); //将创建的线程插入主线程的执行队列,并阻塞主线程,等待该线程执行完再放开。
//thread_oj.detach(); //将创建的线程交由C++运行时库掌管,不阻塞主线程。
return 0;
}
运行结果:
函数浅析
上面我们用C++11的多线程库thread创建了一个线程,肯定有人说:“空口无凭,你怎么敢假定你创建了一个线程呢?” 在上面,我们用了一个函数来验证我们有没有创建线程。
==std::this_thread::get_id()==它的作用是取得它所在运行的线程的id,每个线程都有一个id唯一标识。我们知道,本来该程序是肯定只有一个main()作为主线程的。我们在主线程中放了一个get_id,在我们的所谓线程入口函数里也放了一个get_id,运行结果告诉我们它们并不相同,所以,我们的的确确是创建了一个线程。
==thread thread_oj(可调用对象,[对象的参数])==它的作用是创建一个线程,它有一个参数和多个可选参数,可选参数数量由第一个参数决定。
- 它第一个参数是一个可调用对象,用来作为线程入口函数。创建一个线程,肯定要有它应该要执行的动作,所以该线程将会执行该可调用对象内的语句。除了自定义函数以外,lambda,类的成员函数也是可以作为线程入口函数的。例:==thread myobj5(&A::thread_work, obj, 14);==它用一个名字为A的类的 thread_work(int var) 函数来创建了一个线程。
2.除了第一个参数外,其他参数都是可选参数,要根据线程入口函数的样式来填入。
==thread_oj.join()==它的作用是将我们创建的线程插入线程运行队列,并阻塞主线程,等我们创建的线程执行完再放开。
==thread_oj.detach()==当thread对象使用detach之后,它会失去与主线程的关联,它会被C++运行时库接管,在后台运行,当它执行完毕,由运行时库释放该线程占用的资源,虽然它不归主线程管,但是若主线程退出,它的执行也将结束。(会造成乱序执行,严重会崩溃,可能主线程退出了,但它还没开始运行,它的运行结果不会显示给我们)调用detach之后不能再调用join了
传值陷阱
1.在我们给线程传入一个临时对象作为线程参数时,thread会粗暴的将它进行拷贝复制,将复制后的值传入线程(如果是传入的指针对象,它不会使用拷贝)。如果我们的线程要处理一个在主线程中的临时变量,它可能并不会对主线程中的临时变量产生影响。
2.在线程创建的时候,如果我们使用主线程中的临时变量来构造我们的临时对象,而且该线程调用了detach时。若主线程先执行完了,临时变量已经被回收,那么线程构造调用临时变量时,将拿不到临时变量,将导致未定义的行为,可能产生危险。
注:使用主线程中的临时变量来构造我们的临时对象,临时对象的构造是在子线程中完成构造的。若通过临时变量先生成临时对象,则构造过程是在主线程中完成的
解决方法:在创建线程传入参数的时候,将临时变量预先进行类型转换,直接构造临时对象,这样就可以保证不会出现危险。(detach很容易导致危险行为,尽量少用)。
总结
1:若传递简单类型对象,建议值传递,用引用会导致节外生枝
2:若传递类对象等复杂对象,要避免隐式类型转换,要转换好(转换方式自便),构建出临时对象,然后再函数参数里用引用接受对象,
减少thread函数构造次数
3:尽量避免使用detach,避免临时变量被释放导致出现未定义行为,join多好。