UNIX(多線程):08---線程傳參詳解,detach()陷阱,成員函數做線程函數

2021-02-20 遊戲開發司機
線程傳參詳解,detach()陷阱,成員函數做線程函數傳遞臨時對象作為線程參數

【引例】

#include <iostream>#include <string>#include <thread>using namespace std;void myprint(const int& i, char* pmybuf ) {cout << i << endl;cout << pmybuf << endl;return;}int main(){int val = 1;int& val_y = val;char buf[] = "This is a Test!";thread mythread(myprint, val, buf);  mythread.join();std::cout << "主線程收尾" << std::endl;return 0;}

要避免的陷阱(解釋1)

void myprint(const int i, const string & pmybuf ) {

cout << i << endl;

cout << pmybuf.c_str() << endl;

return;

}

要避免的陷阱(解釋2)

thread mythread(myprint, val, buf); //傳遞參數

thread mythread(myprint, val, string(buf) ); //傳遞參數

#include <iostream>#include <string>#include <thread>using namespace std;class A {public:int m_i;A(int i) :m_i(i) { cout << "A::A(int i)函數執行了" << endl; }A(const A &other) :m_i(other.m_i) { cout << "A::A(const A &other)函數執行了" << endl; }~A() { cout << "A:: ~A()函數執行了" << endl; }};void myprint(const int i, const A & p_a ) {cout << &p_a << endl;  return;}int main(){int m_val = 1;int n_val = 22;thread mythread(myprint, m_val, n_val);  mythread.join();std::cout << "主線程收尾" << std::endl;return 0;}

由輸出可知該構造是發生在main函數執行完畢後才開始的。主線程退出後n_val的內存空間被回收了,此時還用n_val(無效了)去構造A類對象,這會導致一些未定義的行為。

我們期望n_val能夠通過A類的類型轉換構造函數構造出對象,但是遺憾的發現直到主線程退出了都沒構造出我們想要的對象然後傳給子線程。

我們使用顯示地進行轉換,構造出臨時對象,然後調用拷貝構造函數將臨時對象拷貝給線程函數的第二個參數p_a.

thread mythread(myprint, m_val, A(n_val));

總結

臨時對象作為線程參數繼續講線程id概念臨時對象構造時機抓捕

#include <iostream>#include <string>#include <thread>using namespace std;class A {public:int m_i;A(int i) :m_i(i) { cout << "A::A(int i)函數執行了" << this << "  ThreadId  " \<< std::this_thread::get_id()<< endl; }A(const A &other) :m_i(other.m_i) { cout << "A::A(const A &other)函數執行了" << this \<< "  ThreadId  " << std::this_thread::get_id() << endl; }~A() { cout << "A:: ~A()函數執行了" << this << "  ThreadId  "  \<< std::this_thread::get_id() << endl; }};void myprint(const A  &p_a ) {cout << "子線程myprint()參數地址:" << &p_a << "  ThreadId  " \<< std::this_thread::get_id() << endl;return;}int main(){cout << "主線程ID" << std::this_thread::get_id() << endl;int n_val = 69;thread mythread(myprint, n_val);  mythread.join();std::cout << "主線程收尾" << std::endl;return 0;}

thread mythread(myprint, A(n_val));

void myprint(const A p_a )

傳遞類對象、智能指針作為線程參數

在線程中修改變量的值不會影響到主線程。

#include <iostream>#include <string>#include <thread>using namespace std;class A {public:mutable int m_i;A(int i) :m_i(i) { cout << "A::A(int i)函數執行了" << this << "  ThreadId  " \<< std::this_thread::get_id()<< endl; }A(const A &other) :m_i(other.m_i) { cout << "A::A(const A &other)函數執行了" << this \<< "  ThreadId  " << std::this_thread::get_id() << endl; }~A() { cout << "A:: ~A()函數執行了" << this << "  ThreadId  "  \<< std::this_thread::get_id() << endl; }};void myprint(const A  &p_a ) {p_a.m_i = 89;cout << "子線程myprint()參數地址:" << &p_a << "  ThreadId  " \<< std::this_thread::get_id() << endl;return;}int main(){cout << "主線程ID" << std::this_thread::get_id() << endl;A a(1);thread mythread(myprint, a);mythread.join();std::cout << "主線程結束" << std::endl;return 0;}

【std::ref()】

#include <iostream>#include <string>#include <thread>using namespace std;class A {public:int m_i;A(int i) :m_i(i) { cout << "A::A(int i)函數執行了"<< endl; }A(const A &other) :m_i(other.m_i) { cout << "A::A(const A &other)函數執行了"  << endl; }~A() { cout << "A:: ~A()函數執行了"  << endl; }};void myprint(A  &p_a ) {p_a.m_i = 89;cout << "子線程myprint()執行了"  << endl;return;}int main(){A a(1);thread mythread(myprint, std::ref(a));mythread.join();std::cout << "主線程結束" << std::endl;return 0;}

智能指針,想從一個堆棧到另一個堆棧,需要使用std::move()

#include <iostream>#include <string>#include <thread>using namespace std;void myprint(unique_ptr<int> ptr_u) {cout << "子線程myprint()執行了"  << endl;return;}int main(){unique_ptr<int> m_ptr(new int(100));thread mythread(myprint, std::move(m_ptr));mythread.join();std::cout << "主線程結束" << std::endl;return 0;}

用成員函數指針做線程函數

#include <iostream>#include <string>#include <thread>using namespace std;class A {public:int m_i;A(int i) :m_i(i) { cout << "A::A(int i)函數執行了"<< endl; }A(const A &other) :m_i(other.m_i) { cout << "A::A(const A &other)函數執行了"  << endl; }void func(int i) { cout << "A::func(int i)函數執行了" \<< "  i =  " << i << endl; }~A() { cout << "A:: ~A()函數執行了"  << endl; }};int main(){A a_obj(11);thread mythread(&A::func, a_obj, 233);mythread.join();std::cout << "主線程結束" << std::endl;return 0;}

【注】類對象使用引用方式傳遞

thread mythread(&A::func, &a_obj, 233);

thread mythread(&A::func, std::ref(a_obj), 233);

【operator()帶參數】

#include <iostream>#include <string>#include <thread>using namespace std;class A {public:int m_i;A(int i) :m_i(i) { cout << "A::A(int i)函數執行了"<< endl; }A(const A &other) :m_i(other.m_i) { cout << "A::A(const A &other)函數執行了"  << endl; }void operator()(int i) { cout << "A::operator()執行了" \<< "  i =  " << i << endl; }~A() { cout << "A:: ~A()函數執行了"  << endl; }};int main(){A a_obj(11);thread mythread(a_obj, 666);mythread.join();std::cout << "主線程結束" << std::endl;return 0;}

thread mythread(std::ref(a_obj), 999);

thread mythread(&a_obj, 999);

使用detach注意事項小結

相關焦點

  • UNIX(多線程):07---線程啟動、結束,創建線程多法、join,detach
    線程啟動、結束,創建線程多法、join,detach範例演示線程運行的開始和結束#include
  • UNIX(多線程):14---理解線程構造函數
    所以也可以讓它成為線程類的第一個參數,如果這個仿函數有參數,同樣的可以寫在線程類的後幾個參數上。而t2之所以編譯錯誤,是因為編譯器並沒有將Fctor()解釋為一個臨時對象,而是將其解釋為一個函數聲明,編譯器認為你聲明了一個函數,這個函數不接受參數,同時返回一個Factor對象。
  • C++並發與多線程__C++如何線程創建線程以及函數join()和detach()用法和區別
    前言:通常一個程序運行起來,也就等於一個進程在運行,這個進程中會有一個主線程自動創建並運行,當程序的main()函數返回之後那麼此主線程也就運行結束
  • UNIX(多線程):03--- 認識std::thread
    構造函數、拷貝構造函數及析構函數;(2). 成員函數;(3). 靜態成員函數。\n";return EXIT_SUCCESS;}其他成員函數 本小節例子來自 http://en.cppreference.comget_id: 獲取線程 ID,返回一個類型為 std::thread::id 的對象。
  • fork()函數與Linux中的多線程編程
    因為長期以來程序都是單線程的,fork()運轉正常。當20世紀90年代初期引入線程之後,fork()的適用範圍就大為縮小了。在多線程執行的情況下調用fork()函數,僅會將發起調用的線程複製到子進程中。(子進程中該線程的ID與父進程中發起fork()調用的線程ID是一樣的,因此,線程ID相同的情況有時我們需要做特殊的處理。)也就是說不能同時創建出於父進程一樣多線程的子進程。
  • python筆記4-多線程threading之函數式
    1.python環境2.72.threading模塊系統自帶講多線程前,先要了解什麼是進程,什麼是線程,已經知道的請略過。進程是資源分配的最小單位,一個程序至少有一個進程。線程是程序執行的最小單位,一個進程至少有一個線程。
  • 多線程與多進程 | 多線程
    :函數描述start()啟動線程活動。創建一個Thread的實例,傳給它一個可調用的類對象;從Thread派生出一個子類,創建一個這個子類的實例。threading模塊創建線程創建Thread實例,傳給它一個函數import threadingimport timedef run():   # 每個子線程執行輸出三次   for i in range(3):
  • UNIX(多線程):10---線程unique_lock(下)
    obja;std::thread outMsgThread(&A::outMsgRecvQueue, &obja); std::thread inMsgThread(&A::inMsgRecvQueue, &obja);inMsgThread.join();outMsgThread.join();std::cout << "主線程結束
  • Python sleep()函數用法:線程睡眠
    位於 time 模塊中的 sleep(secs) 函數,可以實現令當前執行的線程暫停 secs 秒後再繼續執行。
  • 走進C++11(二十四)一統江湖之線程 -- std::thread
    此頭文件主要聲明了std::thread線程類。C++11的標準類std::thread對線程進行了封裝,定義了C++11標準中的一些表示線程的類、用於互斥訪問的類與方法等。應用C++11中的std::thread便於多線程程序的移值。
  • 多線程的使用
    基於以上場景描述,多線程編程可以完美的解決上述問題。5.1.2 線程概念所謂線程,就是作業系統所能調度的最小單位。普通的進程,只有一個線程在執行對應的邏輯。我們可以通過多線程編程,使一個進程可以去執行多個不同的任務。
  • c++11新特性之線程相關所有知識點
    ,繁瑣且不易讀,c++11引入了std::thread來創建線程,支持對線程join或者detach。func和func1運行在線程對象t和tt中,從剛創建對象開始就會新建一個線程用於執行函數,調用join函數將會阻塞主線程,直到線程函數執行結束,線程函數的返回值將會被忽略。
  • UNIX(多線程):04---Mutex互斥量
    (){ std::thread threads[10]; for (int i=0; i<10; ++i) threads[i] = std::thread(print_thread_id,i+1); for (auto& th : threads) th.join(); return 0;}後邊我將會針對這兩種方式分別做闡述說明
  • 詳解 C++ 多線程的condition_variable
    for (auto & th : threads)  th.join(); return 0;}好了,對條件變量有了一個基本的了解之後,我們來看看 std::condition_variable 的各個成員函數。
  • JavaScript 多線程編程
    通過類似定時器,回調函數等異步編程方式在平常的工作中已經足夠,但是如果做複雜運算,這種方式的不足就逐漸體現出來,比如settimeout拿到的值並不正確,或者頁面有複雜運算的時候很容易觸發假死狀態,異步代碼會影響主線程的代碼執行,異步終究還是單線程,不能從根本上解決問題。
  • 《Exploring in UE4》多線程機制詳解
    不過你可能更在意的是這個所謂多線程的用法,看起來非常簡單,但是卻找不到任何帶有「Thread」或「Runnable」的字樣,那麼它也是用Runnable的方式做的麼?答案肯定是Yes。只不過封裝的比較深,需要我們深入源碼才能明白其中的原理。註:Andriod多線程開發裡面也會用到AsyncTask,二者的實現原理非常相似。
  • Python 多線程基本使用
    不過日常工作中經常只是調用幾個函數來執行多線程,實際上並不理解,經常是面試前突擊,而效果也不理想,結果就是導致低效重複。因此,希望可以系統學習多線程相關知識,並應用於實際工作中,不斷加深理解。首先,多線程的應用場景是什麼呢?下面將首先以一段單線程代碼作為起點,逐步引入多線程。
  • Qt多線程分享——線程局部存儲
    很久沒有更新文章了,後臺一直有很多熱心的小夥伴詢問小豆君,大概是事情比較多,熱情也不如當初了,但是當初自己定下的目標也不能半途而廢,思來想去還是繼續更新文章
  • Python3多線程
    線程:所有的線程都運行在同一個進程當中,共享相同的運行環境。線程有開始、順序執行和結束三個部分, 線程是作業系統能夠進行運算調度的最小單位,它被包含在進程之中,是進程中的實際運作單位。一條線程指的是進程中一個單一順序的控制流,一個進程中可以並發多個線程,每條線程並行執行不同的任務。由於單線程效率低,程序中往往要引入多線程編程。
  • 【程序猿】遊戲引擎Unity中的單線程與多線程
    今天與大家聊一聊我覺得在Unity3D中最基礎也是最重要的概念:Unity3D的單線程與多線程。  概括起來,Unity3D中的子線程無法運行Unity SDK API。  其實,當Unity對其SDK做了這個限制之後,我們可以非常肯定的說Unity是單線程的遊戲引擎。  為什麼要做這個限制?因為遊戲中邏輯更新和畫面更新的時間點要求有確定性,必須按照幀序列嚴格保持同步,否則就會出現遊戲中的對象不同步的現象。