1. 程式人生 > >std::thread(執行緒)

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;
}
  • 結果

這裡寫圖片描述