您的位置:首页 > 编程语言 > C语言/C++

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

2016-06-29 11:32 387 查看
多线程编程引发的问题:死锁、无限延迟、数据竞争等。并发编程包括多任务(多进程)和多线程。

使用多线程编程时需包含头文件<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();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  多线程 C++