C++面向对象多线程编程简介

来源:互联网 发布:什么是网络公关 编辑:程序博客网 时间:2024/06/05 11:15

多线程编程引发的问题:死锁、无限延迟、数据竞争等。并发编程包括多任务(多进程)和多线程。

使用多线程编程时需包含头文件<thread>,具体测试程序代码如下:

#include <iostream>#include <thread>using namespace std;void function_1(){cout<<"Hello world!"<<endl;}void function_2(string str){cout<<str<<endl;}void function_3(string& str){str="我修改了,哈哈!";}void function_4(string str){//str="我修改了,哈哈!";cout<<str<<endl;}class factor{public:void operator()(){cout<<"Thread factor run"<<endl;}};int main(){thread t1(function_1);//通过函数名创建线程 cout<<this_thread::get_id()<<endl;//主线程的id cout<<t1.get_id()<<endl;//t1线程的id factor fac;thread t2(fac);//通过类对象创建线程 //thread t2(factor());//通过调用类函数创建线程 thread t3(function_2,"你好,世界!"); //带有值传递的创建线程 string text="woxiugaile,hehe";thread t5(function_4,move(text));//带有移动传递的创建线程,text被移动后为空t5.join();//若t5还没有执行完成,则主线程等待t5执行完毕再执行。 cout<<text<<endl;thread t4(function_3,ref(text));//若想传递引用时,实参需使用ref(),同时函数形参为&类型 ,能否实现线程间共享内存管理 t4.join(); cout<<text<<endl; for(int i=0;i<10;i++){cout<<"From main: "<<i<<endl;}t3.join();t2.detach();//分离:线程t2与主线程分别进行互不干扰。 t1.detach();for(int i=0;i<100;i++){}if(t1.joinable()){//在join之前判断线程是否可以join,一般detach之后就不能再join t1.join();}cout<<thread::hardware_concurrency()<<endl;//输出计算机的核数,即并发处理时的优化效率最高的最大线程数量 }

以下为数据竞争和互斥对象的说明实例,使用互斥锁需要<mutex>头文件,最基础的方法就是使用lock和unlock方法,使中间部分为临界区,不过这种方法存在临界区部分抛出异常的问题,因此提出了lock_guard<mutex>  guard(mu);的方法,能够在临界区部分抛出异常后解锁,不过由于cout函数是公开的所以依然不能保证打印准确,所以建立一个写文件类保护输出对象,同时成员变量mutex对一些函数的访问可以实现互斥。

#include <iostream>#include <thread>#include <mutex>#include <fstream>using namespace std;mutex mu;void shared_print(string str,int i){lock_guard<mutex> guard(mu);//尽量使用这种保护的互斥锁,因为能够避免加锁后cout抛出异常的情况,lock_guard能够在抛出异常时自动解锁 //mu.lock();//使用mutex互斥锁 ,不过这种直接使用lock和unlock的方式不安全,若出现异常就完了 cout<<str<<":"<<i<<endl;//mu.unlock();//解锁 } void fun1(){for(int i=0;i<10;i++)shared_print("fun1",i);}//以上使用互斥锁虽然能保证函数访问时互斥,不过cout的调用不能保证,所以建立一个输出类,使得写文件对象保证互斥访问。 class lofFile{public:lofFile(){fout.open("log.txt");}void shared_print(string str,int i){lock_guard<mutex> guard(m_mu);fout<<str<<":"<<i<<endl; }private:mutex m_mu;ofstream fout;};void fun2(lofFile& lof){for(int i=0;i<10;i++)lof.shared_print("fun2",i);}int main(){thread t1(fun1);for(int i=0;i>-10;i--)shared_print("main",i);t1.join();//确保t1进程进行完毕lofFile lof;thread t2(fun2,ref(lof));for(int i=0;i>-10;i--)lof.shared_print("main",i);t2.join();}

死锁问题:下例说明了出现死锁的情况,当临界区要求使用2个及以上的互斥锁时,若加锁的顺序不一致,可能导致死锁。因此为避免死锁程序猿要保证加锁的顺序一致,避免发生死锁,也可以使用C++库函数lock函数按照顺序预先加锁,再使用lock_guard智能解锁。

#include <iostream>#include <thread>#include <mutex>#include <fstream>using namespace std; class lofFile{public:lofFile(){fout.open("log.txt");}void shared_print(string str,int i){lock(m_mu,m_mu2);//当一些临界区需要2个及以上的互斥锁时,要保证这些不同临界区的互斥锁的加锁顺序要一致,否则可能导致死锁 ,//为解决这一问题使用lock()函数提前说明加锁顺序,则lock_guard参数adopt_lock说明已经加锁,只需只能解锁即可 lock_guard<mutex> guard(m_mu,adopt_lock);lock_guard<mutex> guard2(m_mu2,adopt_lock);cout<<str<<":"<<i<<endl; }void shared_print2(string str,int i){lock(m_mu,m_mu2);//若不使用lock函数说明加锁顺序也可以,但必须保证以下互斥锁的加锁顺序一致,避免出现死锁情况lock_guard<mutex> guard(m_mu2,adopt_lock);lock_guard<mutex> guard2(m_mu,adopt_lock);cout<<str<<":"<<i<<endl; }private:mutex m_mu;mutex m_mu2; ofstream fout;};void fun2(lofFile& lof){for(int i=0;i<10;i++)lof.shared_print2("fun2",i);}int main(){lofFile lof;thread t2(fun2,ref(lof));for(int i=0;i>-10;i--)lof.shared_print("main",i);t2.join();}
导致死锁的情况:程序一直被挂起。。。


以下为unique_lock和once_flag的使用实例说明,不过unique_lock相对于lock_guard开销更大。

#include <iostream>#include <thread>#include <mutex>#include <fstream>using namespace std; class lofFile{public:lofFile(){//fout.open("log.txt");}void shared_print(string str,int i){call_once(m_flag,[&](){fout.open("log.txt");});//使用once_flag可以只调用依次打开文件函数,从而节省开销 unique_lock<mutex> locker(m_mu);//unique_lock相对于lock_guard具有更好的弹性,从而 能够给确定的代码段加锁//同时,其对象传递时交出所有权,使用defer_lock参数后,就可以使用lock和unlock实现弹性加锁 fout<<str<<":"<<i<<endl; locker.unlock();//.......unique_lock<mutex> locker2(m_mu,defer_lock);//.......locker2.lock();//.......locker2.unlock();//.......}private:mutex m_mu;once_flag m_flag;ofstream fout;};void fun2(lofFile& lof){for(int i=0;i<10;i++)lof.shared_print("fun2",i);}int main(){lofFile lof;thread t2(fun2,ref(lof));for(int i=0;i>-10;i--)lof.shared_print("main",i);t2.join();}


1 0