# Ubuntu上使用C++ muliti - thread筆記 參考 jyt0532's Blog - Introduction to C++ multithread https://hackmd.io/9oWkdjdHRMCqzOBwEI5waA?view#lock_guard ## compile(in C++11 ``` $ g++ filename.cpp -o filename -l pthread ``` ## 使用 先`#include <thread>` 寫一個void function 在thread後面呼叫 ```cpp void thread_func(){ /*~~~~*/ } int main (){ /*~~~~~*/ thread t1(thread_func); //thread會在這裡一呼叫就開始執行,不用等到join t1.join(); //再t1結束之後才可以跑main,否則會abort t1.detach(); //或是可以使用detach,不用等t1結束就可以繼續執行 } ``` ### 傳值與vector (懶得說明了 ```cpp= #include <thread> #include <iostream> #include <string> #include <vector> using namespace std; void func(int i, string s) { cout << i << ", " << this_thread::get_id() << endl; } int main() { vector<thread> threads; for(int i = 0; i < 10; i++){ threads.push_back(thread(func, i, "test")); //我試過用看看array但是我不會 } for(int i = 0; i < threads.size(); i++){ cout << threads[i].get_id() << endl; threads[i].join(); } return 0; } ``` #### ref()是森摸 也是在C++11中的用法,會把值存回main裡的那個值 ```cpp #include <thread> #include <iostream> using namespace std; void func(int a,int &b){ a = 100;b = 200; } int main(){ int a = 1,b = 2; thread t(func,a,ref(b)); t.join(); cout<<"a "<<a<<" b "<< b<<endl; return 0; } ``` 輸出結果 `a 1 b 200` ### lock 在使用thread的時候要注意shared memory會不會爆掉 #### 陽春lock 使用mutex 要`#include <mutex>` ```cpp= #include <thread> #include <iostream> #include <string> #include <vector> #include <mutex> using namespace std; //把mutex和要用的sum包在一起到struct裡面,因為用global variable容易出錯 struct Sum{ int sum = 0; mutex mu; //使用前lock,用完unlock void incre(){ mu.lock(); sum++; mu.unlock(); } }; void func(Sum &s) { s.incre(); } int main() { vector<thread> threads; Sum s; for(int i = 0; i < 10000; i++){ threads.push_back(thread(func, ref(s))); } for(int i = 0; i < threads.size(); i++){ threads[i].join(); } cout << s.sum << endl; return 0; } ``` 如果在lock and unlock中間有錯誤的話,就有可能會永遠無法unlock 可以把中間過程改成try ```cpp= m.lock(); try{ funA(); //可能會出錯的地方 } catch(std::exception &cException){ m.unlock(); throw cException; } m.unlock(); ``` (看後面stack unwinding) 如果中間有很多exception就有可能無法好好release lock(因為unlock可能還來不及執行,程式就被destruct了) 利用在construct和destruct 的時候分配好lock去避免 可以用lock_guard或unique_lock比較方便 #### lock_guard 直接在要執行的thread中使用,但是仍然要先宣告mutex,解構的時候自動unlock ```cpp= mutex m; void thread(){ lock_guard<mutex> lg(m); //lg是自己設定的名字 /*~~~~~~~~*/ } ``` #### unique_lock 也是解構時自動解鎖 很多功能... 可以參考 https://www.twblogs.net/a/5b7dc7612b7177285fcb27dd https://www.itread01.com/content/1546066929.html https://www.itread01.com/content/1544658362.html ```cpp= mutex m1,m2,m3; void thread(){ //(unique_lock...都是初始化一個新的lock然後對某lock操作,這裡是m) unique_lock<mutex> lock1(m1,try_to_lock); //嘗試加鎖 = m.try_lock() unique_lock<mutex> lock2(m2,defer_lock); //在建構時"先"不執行m2.lock() unique_lock<mutex> lock3(m3,adopt_lock); //直接lock /*~~~~~~~~*/ } ``` #### 插播:stack unwinding 是指如果在try的過程中發生exception(自己throw出來),就會直接destruct原本在執行的東西跳出來到catch的地方,之後的Code就不管了,但是會執行destructor ### condition variable 要`#include <condition_variable>` 當作一個queue來放要排隊的thread 功能 ```cpp= condition_variable cv; //declare mutex m; void thread(){ unique_lock<mutex> ul(m); cv.wait(ul) //這個thread被放cv queue中,會自己unlock cv.notify_one() //叫醒cv queue中的第一個thread cv.notify_all() //叫醒所有cv queue中的thread /*~~~~~~~~~~~~*/ } ``` 小截範例 ```cpp= int thread(){ unique_lock<mutex> lk(lock); while(true){ cv1.wait(lk); } /*~~~~*/ lk.unlock(); cv2.notify_one(); /*~~~~*/ return; } ``` 在while裡面叫wait的情形,這個thread會直接跳到cv queue中睡覺(不會待在while裡面跑),等到再次被喚醒時從頭(unique_lock....那裡)開始執行