C++11 ,封装了thread的多线程的类,这样对多线程的使用更加方便。ios
多线程的原理我不加赘述,能够参看操做系统等参考书。多线程
多线程代码能够最大化利用计算机性能资源,提升代码的运行效率,是经常使用优化方法。函数
我不是C++大神,初学阶段的菜鸟而已,不少问题我仍是不理解当中的原理,写这篇博客的缘由,也是记录本身的学习心得和思路,供本身往后本身思考。性能
首先从简单的问题入手,如何写一个多线程的C++代码?学习
#include<iostream> #include<thread> void fun(int a){ a++; } int main(){ int a=0; std::thread t(fun,a); //建立一个线程t,t调用函数fun,a做为fun的参数,也要写到thread的构造函数当中;
t.join(); //启动线程t,而且阻塞主线程,等到线程t运行结束后,再继续运行主线程; std::cout<<a<<std::endl; }
上面这段代码是最简单的多线程代码,调用thread类,并利用了thread的构造函数建立一个线程t,thread类的构造函数重载了不少,后面会继续说到。测试
在这里要说一下,thread类当中的两个成员函数,join()和detach()。这两个成员的做用就像上面代码的注释那样,启动新生成的线程的,可是区别在于join()函数是启动子线程而阻塞主线程,当子线程运行结束后,才会继续运行主线程。相比之下,detach()函数的做用是启动子线程,而且让子线程和主线程分离,子线程和主线程各运行各的,虽然两个线程会由于共享内存池的缘由在操做系统的层面发生发生阻塞等关系,可是在代码层次上,两个线程并不存在谁阻塞谁,极可能主线程已经运行结束了,子线程还在运行。优化
接下来,咱们要说一下类当中的成员函数如何初始化thread类的构造函数。this
对于类的成员函数,咱们须要给出类对象的地址:spa
#include<iostream> #include<thread> class A{ public: void fun(int a,int b){ std::cout<<"this is A thread!"<<a<<std::endl; } }; int main(){ int k=0; A a; std::thread t(&A::fun,a,k,k+1); t.join(); }
std::thread t(&A::fun,a,k,k+1); 这个地方就能够看出thread类的构造对于成员函数的重载了,std::thread t(函数(成员函数)地址,对象地址,成员函数的参数1,参数2,参数3...)。
相比非成员函数,成员函数须要给出类实例化对象的地址,若是该线程是在同一类的某一成员函数当中被构造,则直接用this关键字代替便可。
其实,我在写成员函数的多线程代码的时候,发现成员函数的须要传递的参数太多会使thread类的构造函数重载失败,我测试了一下,成员函数最多只能传递4个参数,也就说std::thread类的构造函数最多只能重载6个参数。
这一点,我并无找到相关文档获得证明,只是在写代码的时候发现成员函数传递参数太多,会一直编译不经过,偶然间发现这个点的,具体到底对不对,我也不是很肯定。
其次,咱们要说一下加锁和解锁的问题。
由于咱们创造的每个线程只要在一个进程内,都是共享内存池的,这样在读写数据可能会发生混乱。
C++11提供了mutex类进行加锁和解锁。
#include<iostream> #include<thread> #include<mutex> std::mutex mut; class A{ public: volatile int temp; A(){ temp=0; } void fun(int num){ int count=10; while(count>0){ mut.lock(); temp++; std::cout<<"thread_"<<num<<"...temp="<<temp<<std::endl; mut.unlock(); count--; } } void thread_run(){ std::thread t1(&A::fun,this,1); std::thread t2(&A::fun,this,2); t1.join(); t2.join(); } }; int main(){ A a; a.thread_run(); }
而后,咱们说一下volatile关键字。
volatile和const关键很类似,都是修饰变量的,只是两者功能不同。
volatile在多线程当中常常使用,由于在某一线程屡次调用某一个变量,编译器会进行优化,将该变量存放在在寄存器当中,不会每次都从内存当中读入。果真该变量同时在其余线程当中被修改,这样就会发生脏读取错误。
而加上volatile修饰,则会提醒编译器,这个变量可能会被改变,不能存放到寄存器当中,须要每次都从内存当中读取。
最后,咱们说一下join()和detach()的使用技巧。