當前位置:
首頁 > 知識 > C++11多線程 多線程傳參詳解

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;

}

C++11多線程 多線程傳參詳解

打開今日頭條,查看更多圖片

---------------------

作者:u012507022

原文:https://blog.csdn.net/u012507022/article/details/85845799

版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 程序員小新人學習 的精彩文章:

C++隨機數生成器的手動實現與泊松分布
Swift中由找不到removeAll(where:)方法引起的連鎖反應(上)

TAG:程序員小新人學習 |