C++11多線程 多線程傳參詳解
1.傳遞臨時對象做線程參數
1.1要避免的陷阱1
用detach()時,如果主線程先結束,變數就會被回收;所以用detach()的話,不推薦用引用,同時絕對不能用指針。
1.2要避免的陷阱2
只要臨時對象的用臨時構造A類對象作為參數傳遞給線程,那麼就一定能夠在主線程結束之前,把線程函數的第二個參數構建出來,從而確保即便detach()子線程也安全運行,程序如下:
#include<iostream>
#include<thread>
#include<string>
using namespace std;
class A
{
public:
int m_i;
//類型轉換構造函數,可以把一個int轉換成一個類A對象。
A(int a):m_i(a){cout << "A::A(int a)構造函數執行!"<< endl;}
A(const A &a) :m_i(a.m_i){cout << "A::A(A &a)複製構造函數執行!" << endl;}
~A(){cout << "A::~A()析構函數執行!" << endl;}
};
void myprint1(const int &i, char *pmybuf)
{
//通過查看內存可知變數mvar與它的引用mvary地址是相同的,但是傳遞到myprint()中,
//&i的地址不是mvar的地址,所以這是個假引用,此時引用傳遞與傳值是一樣的。
//分析可得,並不是mvar的引用,實際是值傳遞,那麼我們認為,即便是主線程detach了子線程,
//那麼子線程用i值任然是安全的!
cout << i << endl;
//第二個參數*pmybuf 是指針,*pmybuf的地址與mybuf[]相同;
//指針在detach子線程時,絕對會有問題;
cout << pmybuf << endl;
}
//此時用 string& 通過一個隱形轉換來接收mybuf[]的值,但此時安全嗎?
//但是mybuf是 在什麼時候轉換成string?
//事實上存在mybuf都被回收了,系統才將mybuf去轉string的可能性
//所以此方法也是不安全的
void myprint2(const int i, const string &pmybuf)
{
cout << i << endl;
cout << pmybuf << endl;
}
int main()
{
int mvar = 1;
int &mvary = mvar;
char mybuf[] = "This is a test!";
//thread myobj(myprint1, mvar, mybuf);
//string(mybuf)生成臨時string對象;我們這裡直接將mybuf轉換成string對象,
//這是一個可以保證在線程中用肯定有效的對象。
//下個程序進行驗證
thread myobj(myprint2, mvar, string(mybuf));
myobj.detach();
cout << "主線程執行!" << endl;
system("pause");
return 0;
}
通過自定義類的方式驗證 臨時對象可以保證主線程結束之前,把線程函數的參數構建出來
#include<iostream>
#include<thread>
#include<string>
using namespace std;
class A
{
public:
int m_i;
//類型轉換構造函數,可以把一個int轉換成一個類A對象。
A(int a) :m_i(a) { cout << "A::A(int a)構造函數執行!" << endl; }
A(const A &a) :m_i(a.m_i) { cout << "A::A(A &a)複製構造函數執行!" << endl; }
~A() { cout << "A::~A()析構函數執行!" << endl; }
};
void myprint(const int i, const A &pmybuf)
{
cout << &pmybuf << endl;// 列印的是pmybuf對象的地址
}
int main()
{
int mvar = 1;
int mysecondpar = 12;
//我們希望mysecondpar轉成A類型對象傳遞給myprint的第二個參數
/*主線程什麼都不操作,快速結束主線程,運行程序後可以發現,
沒有輸出「A::A(int a)構造函數執行!」說明主線程執行完畢,*/
thread myobj(myprint, mvar, mysecondpar);
/*在創建線程的同時構造臨時對象的方法傳遞參數是可行的!
//可以保證在主線程結束之前,構造出來!*/
thread myobj(myprint, mvar, A(mysecondpar));
myobj.detach();
//cout << "主線程執行!" << endl;
return 0;
}
1.3總結
若傳遞int這種簡單類型參數,建議都是值傳遞,不要引用,防止節外生枝
如果傳遞類對象,避免隱式類型轉換。全部都在創建線程這一行就構建出臨時對象,然後在函數參數里用引用來接;否則系統還會多構造一次對象。
終極結論:建議不使用detach(),只使用join();這樣就不存在局部變數失效導致線程對內存的非法引用問題。
2.臨時對象作為線程參數繼續
2.1線程ID概念
Id是個數字,每一個線程(不管是主線程還是子線程)實際上都對應一個數字,而且每一個線程對應的這個數字都不同。也就是說,不同的線程,他的線程id必然不同;
線程ID可以用C++標準庫里的函數來獲取。std::this_thread::get_id()來獲取。
2.2臨時對象構造時機抓捕
通過這個例子也可以看出,臨時對象後在main()函數中已經構造完畢了。
#include<iostream>
#include<thread>
#include<string>
using namespace std;
class A
{
public:
int m_i;
//類型轉換構造函數,可以把一個int轉換成一個類A對象。
A(int a) :m_i(a)
{
cout << "A::A(int a)構造函數執行!"<<this<<"threadid:" <<std::this_thread::get_id()<< endl;
}
A(const A &a) :m_i(a.m_i)
{
cout << "A::A(A &a)複製構造函數執行!" << this << "threadid:" << std::this_thread::get_id() << endl;
}
~A()
{
cout << "A::~A()析構函數執行!" << this << "threadid:" << std::this_thread::get_id() << endl;
}
};
void myprint2(const A &pmybuf)
{
cout << "子對象myprint的參數地址是" <<&pmybuf<<"threadid"<<std::this_thread::get_id()<<endl;// 列印的是pmybuf對象的地址
}
int main()
{
cout << "主線程id:" << std::this_thread::get_id() <<endl;
int mvar = 2;
//thread myobj(myprint2, mvar); //致命問題是在子線程中構造A類對象
thread myobj(myprint2, A(mvar)); //用了臨時對象後,所有的A類對象都在main()函數中已經構造完畢了
myobj.join();
//myobj.detach(); //子線程與主線程分別執行
return 0;
}
3.傳遞類對象、智能指針作為線程參數
std::ref()函數的作用 可以實現真正的引用
#include<iostream>
#include<thread>
#include<string>
using namespace std;
class A
{
public:
mutable int m_i;
//類型轉換構造函數,可以把一個int轉換成一個類A對象。
A(int a) :m_i(a)
{
cout << "A::A(int a)構造函數執行!" << this << "threadid:" << std::this_thread::get_id() << endl;
}
A(const A &a) :m_i(a.m_i)
{
cout << "A::A(A &a)複製構造函數執行!" << this << "threadid:" << std::this_thread::get_id() << endl;
}
~A()
{
cout << "A::~A()析構函數執行!" << this << "threadid:" << std::this_thread::get_id() << endl;
}
};
void myprint2(const A &pmybuf)
{
pmybuf.m_i = 199; //我們修改該值不會影響main()函數
cout << "子對象myprint的參數地址是" << &pmybuf << "threadid" << std::this_thread::get_id() << endl;// 列印的是pmybuf對象的地址
}
int main()
{
A myobj(10); //生成一個類對象
thread mytobj(myprint2, std::ref(myobj));
mytobj.join();
return 0;
}
#include<iostream>
#include<thread>
#include<string>
using namespace std;
void myprint2(unique_ptr<int> pzn)
{
;
}
int main()
{
unique_ptr<int> myp(new int(100));
thread mytobj(myprint2,std::move(myp));
mytobj.join();
return 0;
}
4. 用成員函數指針做線程函數
#include<iostream>
#include<thread>
#include<string>
using namespace std;
class A
{
public:
mutable int m_i;
//類型轉換構造函數,可以把一個int轉換成一個類A對象。
A(int a) :m_i(a)
{
cout << "A::A(int a)構造函數執行!" << this << "threadid:" << std::this_thread::get_id() << endl;
}
A(const A &a) :m_i(a.m_i)
{
cout << "A::A(A &a)複製構造函數執行!" << this << "threadid:" << std::this_thread::get_id() << endl;
}
~A()
{
cout << "A::~A()析構函數執行!" << this << "threadid:" << std::this_thread::get_id() << endl;
}
void thread_work(int num)
{
cout << "子線程thread——work執行!" << this << "threadid:" << std::this_thread::get_id() << endl;
}
};
int main()
{
A myobj(10);
thread mytobj(&A::thread_work, &myobj, 15);
mytobj.join();
return 0;
}
打開今日頭條,查看更多圖片---------------------
作者:u012507022
原文:https://blog.csdn.net/u012507022/article/details/85845799
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!
※C++隨機數生成器的手動實現與泊松分布
※Swift中由找不到removeAll(where:)方法引起的連鎖反應(上)
TAG:程序員小新人學習 |