C++11多執行緒基礎
1. join
#include <iostream> #include <thread> using namespace std; void fun() //子執行緒 { for (int i = 0; i < 100; i++) cout << "*"; } void main() { thread t(fun); //建立執行緒,執行緒從fun()函式開始執行 t.join(); //阻塞主執行緒,讓主執行緒等待子執行緒執行完畢 for (int i = 0; i < 100; i++) //主執行緒cout << "$"; }
輸出:****************************************************************************************************$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
#include <iostream> #include <thread> using namespacestd; void fun() //子執行緒 { for (int i = 0; i < 100; i++) cout << "*"; } void main() { thread t(fun); //建立執行緒,執行緒從fun()函式開始執行 for (int i = 0; i < 100; i++) //主執行緒 cout << "$"; t.join(); //阻塞主執行緒,讓主執行緒等待子執行緒執行完畢 }
輸出:
*****************************************$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*******************$$$$$$$$$*************************************$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$***
#include <iostream> #include <thread> using namespace std; void fun() //子執行緒 { for (int i = 0; i < 100; i++) cout << "*"; } void main() { thread t(fun); //建立執行緒,執行緒從fun()函式開始執行 for (int i = 0; i < 100; i++) //主執行緒 cout << "$"; }
輸出:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16$$ 17$$ 18 19 20 21 22 23 24 25 26 $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$27
2. detach
#include <iostream> #include <thread> using namespace std; void fun() //子執行緒 { for (int i = 0; i < 100; i++) cout << i << " "; } void main() { thread t(fun); //建立執行緒,執行緒從fun()函式開始執行 t.detach(); //主執行緒不等待子執行緒執行完畢就可以先行退出,子執行緒駐留在後臺執行 for (int i = 0; i < 100; i++) //主執行緒 cout << "$"; }
輸出:
0 1 2 3 4 $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$5
3. joinable
#include <iostream> #include <thread> using namespace std; void fun() //子執行緒 { for (int i = 0; i < 100; i++) cout << i << " "; } void main() { thread t(fun); //建立執行緒,執行緒從fun()函式開始執行 if (t.joinable()) //判斷是否可以成功使用join()或者detach(),呼叫join後不能再呼叫join和detach,呼叫detach後也不能再呼叫join和detach。因為重複呼叫會導致異常 { t.join(); cout << "joinable() == true" << endl; } for (int i = 0; i < 100; i++) //主執行緒 cout << "$"; }
輸出:****************************************************************************************************$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
4. 可呼叫物件
#include <iostream> #include <thread> using namespace std;class A { public: void operator()() //不能帶引數 { for (int i = 0; i < 100; i++) //子執行緒 cout << i << " "; } }; void main() { A a; //a:可呼叫物件 thread t(a); //將物件a複製到子執行緒中,執行完主執行緒後,物件a會被銷燬,但是複製到子執行緒中的物件依舊存在。只要這個A類物件沒有主執行緒中的引用或指標,就不會出現問題。 t.join(); //阻塞主執行緒,讓主執行緒等待子執行緒執行完畢 for (int i = 0; i < 100; i++) //主執行緒 cout << "$"; }
輸出:****************************************************************************************************$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
5.lambda表示式
#include <iostream> #include <thread> using namespace std;void main() { auto lamthread = [] { for (int i = 0; i < 100; i++) cout << i << " "; }; thread t(lamthread); t.join(); }
輸出:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
6. 陷阱
#include <iostream> #include <thread> using namespace std; void fun(const int& i, char* buf) //子執行緒 { cout << i << endl; cout << buf << endl; } void main() { int num = 9; char buf[] = "abc"; thread t(fun, num, buf); t.join(); cout << "i love you" << endl; }
輸出:
9
abc
i love you
#include <iostream> #include <thread> using namespace std; void fun(const int& i, char* buf) //子執行緒 { cout << i << endl; //i並不是num的引用,實際是值傳遞。即使主執行緒使用detach,子執行緒中的i也是安全的。最好不使用引用。 cout << buf << endl; //指標在主執行緒使用detach時,絕對會有問題。因為主執行緒中的buf已經被釋放,所以子執行緒中的buf就變成了野指標。 } void main() { int num = 9; char buf[] = "abc"; thread t(fun, num, buf); //有問題 t.detach(); }
#include <iostream> #include <thread> using namespace std; void fun(const int& i, string& buf) //子執行緒 { cout << i << endl; cout << buf.c_str() << endl; } void main() { int num = 9; char buf[] = "abc"; thread t(fun, num, string(buf)); //沒問題。用臨時構造的string類物件作為引數傳遞給執行緒,一定能在主執行緒執行完畢前把執行緒函式的第二個引數構造出來,從而確保主執行緒即便detach了子執行緒也能安全執行。
t.detach();
}
#include <iostream> #include <thread> using namespace std; class A { public: //型別轉換建構函式,可以把一個int轉換成一個類A物件 A(int i) :m_i(i) { cout << "呼叫建構函式" << this << endl; } A(const A& a) :m_i(a.m_i) { cout << "呼叫拷貝建構函式}" << this << endl; } A() { cout << "呼叫解構函式" << this << endl; } private: int m_i; }; void fun(const int& i, const A& a) //子執行緒 { cout << i << endl; cout << &a << endl; //輸出物件地址 } void main() { int num = 9;int sum = 0; thread t(fun, num, sum); //有時候都還沒呼叫建構函式主執行緒就退出了,則會有問題。需要改成建立臨時物件thread t(fun, num, A(sum)); t.detach(); }
#include <iostream> #include <thread> using namespace std; class A { public: //型別轉換建構函式,可以把一個int轉換成一個類A物件 A(int i) :m_i(i) { cout << "呼叫建構函式" << this << endl; } A(const A& a) :m_i(a.m_i) { cout << "呼叫拷貝建構函式}" << this << endl; } A() { cout << "呼叫解構函式" << this << endl; } private: int m_i; }; void fun(const int& i, const A& a) //子執行緒 { cout << i << endl; cout << &a << endl; //輸出物件地址 } void main() { int num = 9;int sum = 0; thread t(fun, num, A(sum)); //絕對沒問題。絕對在先呼叫建構函式後再退出主執行緒 t.detach(); }
輸出:
呼叫建構函式00ADF884
呼叫拷貝建構函式}00E54B58
9
8. 執行緒id
驗證:
#include <iostream> #include <thread> using namespace std; class A { public: //型別轉換建構函式,可以把一個int轉換成一個類A物件 A(int i) :m_i(i) { cout << "呼叫建構函式" << this << " threadid:" << std::this_thread::get_id() << endl; } A(const A& a) :m_i(a.m_i) { cout << "呼叫拷貝建構函式}" << this << " threadid:" << std::this_thread::get_id() << endl; } A() { cout << "呼叫解構函式" << this << " threadid:" << std::this_thread::get_id() << endl; } private: int m_i; }; void fun(const A& a) //子執行緒 { cout << "a threadid:" << std::this_thread::get_id() << endl; cout << &a << endl; //輸出物件地址 } void main() { cout << "主執行緒id:" << std::this_thread::get_id() << endl; int num = 9;int sum = 0; thread t(fun, sum); //在子執行緒中構造的A類物件。當呼叫detach時顯然會出問題,因為sum已釋放,則子執行緒中的sum就是不可預知的值。 t.join(); }
輸出:
主執行緒id:5300
呼叫建構函式00CCF7E4 threadid:6380
a threadid:6380
00CCF7E4
#include <iostream> #include <thread> using namespace std; class A { public: //型別轉換建構函式,可以把一個int轉換成一個類A物件 A(int i) :m_i(i) { cout << "呼叫建構函式" << this << " threadid:" << std::this_thread::get_id() << endl; } A(const A& a) :m_i(a.m_i) { cout << "呼叫拷貝建構函式}" << this << " threadid:" << std::this_thread::get_id() << endl; } A() { cout << "呼叫解構函式" << this << " threadid:" << std::this_thread::get_id() << endl; } private: int m_i; }; void fun(const A& a) //子執行緒。 用引用傳遞,否則得多呼叫一次拷貝建構函式 { cout << "a threadid:" << std::this_thread::get_id() << endl; cout << &a << endl; //輸出物件地址 } void main() { cout << "主執行緒id:" << std::this_thread::get_id() << endl; int num = 9; int sum = 0; thread t(fun, A(sum)); //在主執行緒中構造的A類物件。當呼叫detach時也不會有問題。 t.join(); }
輸出:
主執行緒id:18328
呼叫建構函式003CF874 threadid:18328
呼叫拷貝建構函式}0043AD88 threadid:18328
a threadid:14056
0043AD88
8.