1. 程式人生 > >C++ thread用法總結(整理)

C++ thread用法總結(整理)

1,簡介

C++11中加入了<thread>標頭檔案,此標頭檔案主要聲明瞭std::thread執行緒類。C++11的標準類std::thread對執行緒進行了封裝,定義了C++11標準中的一些表示執行緒的類、用於互斥訪問的類與方法等。應用C++11中的std::thread便於多執行緒程式的移值。

std::thread類成員函式:

(1)、get_id:獲取執行緒ID,返回一個型別為std::thread::id的物件。

(2)、joinable:檢查執行緒是否可被join。檢查thread物件是否標識一個活動(active)的可行性執行緒。預設構造的thread物件、已經完成join的thread物件、已經detach的thread物件都不是joinable。

(3)、join:呼叫該函式會阻塞當前執行緒。阻塞呼叫者(caller)所在的執行緒直至被join的std::thread物件標識的執行緒執行結束。

(4)、detach:將當前執行緒物件所代表的執行例項與該執行緒物件分離,使得執行緒的執行可以單獨進行。一旦執行緒執行完畢,它所分配的資源將會被釋放。

(5)、native_handle:該函式返回與std::thread具體實現相關的執行緒控制代碼。native_handle_type是連線thread類和作業系統SDK API之間的橋樑,如在Linux g++(libstdc++)裡,native_handle_type其實就是pthread裡面的pthread_t型別,當thread類的功能不能滿足我們的要求的時候(比如改變某個執行緒的優先順序),可以通過thread類例項的native_handle()返回值作為引數來呼叫相關的pthread函式達到目錄。This member function is only present in class thread if the library implementation supports it. If present, it returns a value used to access implementation-specific information associated to the thread.

(6)、swap:交換兩個執行緒物件所代表的底層控制代碼。

(7)、operator=:moves the thread object

(8)、hardware_concurrency:靜態成員函式,返回當前計算機最大的硬體併發執行緒數目。基本上可以視為處理器的核心數目。

另外,std::thread::id表示執行緒ID,定義了在執行時作業系統內唯一能夠標識該執行緒的識別符號,同時其值還能指示所標識的執行緒的狀態。Values of this type are returned by thread::get_id and this_thread::get_id to identify threads.

有時候我們需要線上程執行程式碼裡面對當前呼叫者執行緒進行操作,針對這種情況,C++11裡面專門定義了一個名稱空間this_thread,此名稱空間也宣告在<thread>標頭檔案中,其中包括get_id()函式用來獲取當前呼叫者執行緒的ID;yield()函式可以用來將呼叫者執行緒跳出執行狀態,重新交給作業系統進行排程,即當前執行緒放棄執行,作業系統排程另一執行緒繼續執行;sleep_until()函式是將執行緒休眠至某個指定的時刻(time point),該執行緒才被重新喚醒;sleep_for()函式是將執行緒休眠某個指定的時間片(time span),該執行緒才被重新喚醒,不過由於執行緒排程等原因,實際休眠實際可能比sleep_duration所表示的時間片更長。

1.建立一個執行緒

建立執行緒比較簡單,使用std的thread例項化一個執行緒物件就建立完成了,示例:

#include <iostream>
#include <thread>
#include <stdlib.h> //sleep

using namespace std;

void t1()  //普通的函式,用來執行執行緒
{
    for (int i = 0; i < 10; ++i)
    {
        cout << "t1111\n";
        sleep(1);
    }
}
void t2()
{
    for (int i = 0; i < 20; ++i)
    {
        cout << "t22222\n";
        sleep(1);
    }
}
int main()
{
    thread th1(t1);  //例項化一個執行緒物件th1,使用函式t1構造,然後該執行緒就開始執行了(t1())
    thread th2(t2);

    th1.join(); // 必須將執行緒join或者detach 等待子執行緒結束主程序才可以退出
    th2.join(); 

    //or use detach
    //th1.detach();
    //th2.detach();

    cout << "here is main\n\n";

    return 0;
}

上述提到的問題,還可以使用detach來解決,detach是用來和執行緒物件分離的,這樣執行緒可以獨立地執行,不過這樣由於沒有thread物件指向該執行緒而失去了對它的控制,當物件析構時執行緒會繼續在後臺執行,但是當主程式退出時並不能保證執行緒能執行完。如果沒有良好的控制機制或者這種後臺執行緒比較重要,最好不用detach而應該使用join。

2, mutex和std::lock_guard的使用

標頭檔案是#include <mutex>,mutex是用來保證執行緒同步的,防止不同的執行緒同時操作同一個共享資料。

但使用lock_guard則相對安全,它是基於作用域的,能夠自解鎖,當該物件建立時,它會像m.lock()一樣獲得互斥鎖,當生命週期結束時,它會自動析構(unlock),不會因為某個執行緒異常退出而影響其他執行緒。示例:

#include <iostream>
#include <thread>
#include <mutex>
#include <stdlib.h>

int cnt = 20;
std::mutex m;
void t1()
{
    while (cnt > 0)
    {    
        std::lock_guard<std::mutex> lockGuard(m);
       // std::m.lock();
        if (cnt > 0)
        {
            //sleep(1);
            --cnt;
            std::cout << cnt << std::endl;
        }
       // std::m.unlock();
        
    }
}
void t2()
{
    while (cnt > 0)
    {
        std::lock_guard<std::mutex> lockGuard(m);
        // std::m.lock();
        if (cnt > 0)
        {
            --cnt;
            std::cout << cnt << std::endl;
        }
        // std::m.unlock();
    }
}

int main(void)
{
	std::thread th1(t1);
	std::thread th2(t2);

	th1.join();    //等待t1退出
	th2.join();    //等待t2退出

	std::cout << "here is the main()" << std::endl;

	return 0;
}

輸出結果,cnt是依次遞減的,沒有因為多執行緒而打亂次序::

19
18
17
16
15
14
13
12
11
10
9
8
7
6
5
4
3
2
1
0
here is the main()