std::thread(執行緒)
建立執行緒
建立執行緒比較簡單,C++提供標頭檔案thread,使用std的thread例項化一個執行緒物件建立。
std::thread 在 #include 標頭檔案中宣告,因此使用 std::thread 時需要包含 #include 標頭檔案。
#include<iostream>
#include<thread>
using namespace std;
void thread1() {
for(int i=0;i<20;++i)
cout << "thread1..." << endl;
}
void thread2() {
for (int i = 0; i<20; ++i)
cout << "thread2..." << endl;
}
int main(int argc, char* argv[]) {
thread th1(thread1); //例項化一個執行緒物件th1,該執行緒開始執行
thread th2(thread2);
cout << "main..." << endl;
return 0;
}
- 結果分析:上例是有問題的,因為在建立了執行緒後執行緒開始執行,但是主執行緒main()並沒有停止腳步,仍然繼續執行然後退出,此時執行緒物件還是joinable(可結合的),執行緒仍然存在但指向它的執行緒物件已經銷燬,所以會中斷。
- 那麼該如何保證子執行緒執行完了退出後再退出主執行緒呢?
解決方法一:thread::join()
- 使用join介面可以解決上述問題,join的作用是讓主執行緒等待直到該子執行緒執行結束。
#include<iostream>
#include<thread>
using namespace std;
void thread1() {
for(int i=0;i<20;++i)
cout << "thread1..." << endl;
}
void thread2() {
for (int i = 0; i<20; ++i)
cout << "thread2..." << endl;
}
int main(int argc, char* argv[]) {
thread th1(thread1); //例項化一個執行緒物件th1,該執行緒開始執行
thread th2(thread2);
cout << "****************" << th1.joinable() << endl;
th1.join();
cout << "****************" << th1.joinable() << endl;
th2.join();
cout << "main..." << endl;
return 0;
}
結果分析:此時就可以正常地執行子執行緒了,同時注意最後一個輸出,說明了main是等待子執行緒結束才繼續執行的。
需要注意的是執行緒物件執行了join後就不再joinable(判斷執行緒是否可以加入等待)了,所以只能呼叫join一次。
joinable: 檢查執行緒是否可被 join。檢查當前的執行緒物件是否表示了一個活動的執行執行緒,由預設建構函式建立的執行緒是不能被 join 的。另外,如果某個執行緒已經執行完任務,但是沒有被 join 的話,該執行緒依然會被認為是一個活動的執行執行緒,因此也是可以被 join 的。
解決方法一:thread::detach()
將當前執行緒物件所代表的執行例項與該執行緒物件分離,使得執行緒的執行可以單獨進行。一旦執行緒執行完畢,它所分配的資源將會被釋放。
detach是用來分離執行緒,這樣執行緒可以獨立地執行,不過這樣由於沒有thread物件指向該執行緒而失去了對它的控制,當物件析構時執行緒會繼續在後臺執行,但是當主程式退出時並不能保證執行緒能執行完。如果沒有良好的控制機制或者這種後臺執行緒比較重要,最好不用detach而應該使用join。
#include<iostream>
#include<thread>
using namespace std;
void thread1() {
for(int i=0;i<20;++i)
cout << "thread1..." << endl;
}
void thread2() {
for (int i = 0; i<20; ++i)
cout << "thread2..." << endl;
}
int main(int argc, char* argv[]) {
thread th1(thread1); //例項化一個執行緒物件th1,該執行緒開始執行
thread th2(thread2);
th1.detach();
th2.detach();
cout << "main..." << endl;
return 0;
}
- 結果分析:執行緒未執行完退出。
mutex
標頭檔案是,mutex是用來保證執行緒同步的,防止不同的執行緒同時操作同一個共享資料。
#include:該標頭檔案主要聲明瞭與互斥量(mutex)相關的類,包括 std::mutex 系列類,std::lock_guard, std::unique_lock, 以及其他的型別和函式。
#include<iostream>
#include<thread>
#include<mutex>
using namespace std;
mutex m;
int cnt = 10;
void thread1() {
while (cnt > 5){
m.lock();
if (cnt > 0) {
--cnt;
cout << cnt << endl;
}
m.unlock();
}
}
void thread2() {
while (cnt > 0) {
m.lock();
if (cnt > 0) {
cnt -= 10;
cout << cnt << endl;
}
m.unlock();
}
}
int main(int argc, char* argv[]) {
thread th1(thread1); //例項化一個執行緒物件th1,該執行緒開始執行
thread th2(thread2);
th1.join();
th2.join();
cout << "main..." << endl;
return 0;
}
- 結果分析:mutex是不安全的,當一個執行緒在解鎖之前異常退出了,那麼其它被阻塞的執行緒就無法繼續下去。
std::lock_guard
- 使用lock_guard則相對安全,它是基於作用域的,能夠自解鎖,當該物件建立時,它會像m.lock()一樣獲得互斥鎖,當生命週期結束時,它會自動析構(unlock),不會因為某個執行緒異常退出而影響其他執行緒。
#include<iostream>
#include<thread>
#include<mutex>
using namespace std;
mutex m;
int cnt = 10;
void thread1() {
while (cnt > 5){
lock_guard<mutex> lockGuard(m);
if (cnt > 0) {
--cnt;
cout << cnt << endl;
}
}
}
void thread2() {
while (cnt > 0) {
lock_guard<mutex> lockGuard(m);
if (cnt > 0) {
cnt -= 10;
cout << cnt << endl;
}
}
}
int main(int argc, char* argv[]) {
thread th1(thread1); //例項化一個執行緒物件th1,該執行緒開始執行
thread th2(thread2);
th1.join();
th2.join();
cout << "main..." << endl;
return 0;
}
- 結果
其他
get_id: 獲取執行緒 ID,返回一個型別為 std::thread::id 的物件。
swap():交換兩個執行緒物件所代表的底層控制代碼(underlying handles)。
拷貝建構函式是被禁用的:thread (const thread&) = delete;
#include<iostream>
#include<thread>
#include<mutex>
using namespace std;
mutex m;
int cnt = 10;
void thread1() {
while (cnt > 5){
lock_guard<mutex> lockGuard(m);
if (cnt > 0) {
--cnt;
cout << cnt << endl;
}
}
}
void thread2() {
while (cnt > 0) {
lock_guard<mutex> lockGuard(m);
if (cnt > 0) {
cnt -= 10;
cout << cnt << endl;
}
}
}
int main(int argc, char* argv[]) {
thread th1(thread1); //例項化一個執行緒物件th1,該執行緒開始執行
thread th2(thread2);
thread th3(th1); //拷貝建構函式被刪除
cout << "******" << "th1 id: " << th1.get_id() << "******" << endl;
cout << "******" << "th2 id: " << th2.get_id() << "******" << endl;
th1.swap(th2);
cout << "******" << "th1 id: " << th1.get_id() << "******" << endl;
cout << "******" << "th2 id: " << th2.get_id() << "******" << endl;
th1.join();
th2.join();
cout << "main..." << endl;
return 0;
}
- 結果