天天看點

C++11線程使用總結

C++11線程使用總結

std::thread 在 <thread> 頭檔案中聲明,是以使用 std::thread 需包含 <thread> 頭檔案。

<thread> 頭檔案摘要

<thread> 頭檔案聲明了 std::thread 線程類及 std::swap (交換兩個線程對象)輔助函數。另外命名空間 std::this_thread 也聲明在 <thread> 頭檔案中。下面是 C++11 标準所定義的 <thread> 頭檔案摘要:

參見 N3242=11-0012 草案第 30.3 節 Threads(p1133)。

<b>[cpp]</b> view plain copy

amespace std {

    #define __STDCPP_THREADS__ __cplusplus

    class thread;

    void swap(thread&amp; x, thread&amp; y);

    namespace this_thread {

        thread::id get_id();

        void yield();

        template &lt;class Clock, class Duration&gt;

        void sleep_until(const chrono::time_point&lt;Clock, Duration&gt;&amp; abs_time);

        template &lt;class Rep, class Period&gt;

        void sleep_for(const chrono::duration&lt;Rep, Period&gt;&amp; rel_time);

    }

}

&lt;thread&gt; 頭檔案主要聲明了 std::thread 類,另外在 std::this_thread 命名空間中聲明了get_id,yield,sleep_until 以及 sleep_for 等輔助函數,本章稍微會詳細介紹 std::thread 類及相關函數。

std::thread 類摘要

std::thread 代表了一個線程對象,C++11 标準聲明如下:

namespace std {

    class thread {

        public:

            // 類型聲明:

            class id;

            typedef implementation-defined native_handle_type;

            // 構造函數、拷貝構造函數和析構函數聲明:

            thread() noexcept;

            template &lt;class F, class ...Args&gt; explicit thread(F&amp;&amp; f, Args&amp;&amp;... args);

            ~thread();

            thread(const thread&amp;) = delete;

            thread(thread&amp;&amp;) noexcept;

            thread&amp; operator=(const thread&amp;) = delete;

            thread&amp; operator=(thread&amp;&amp;) noexcept;

            // 成員函數聲明:

            void swap(thread&amp;) noexcept;

            bool joinable() const noexcept;

            void join();

            void detach();

            id get_id() const noexcept;

            native_handle_type native_handle();

            // 靜态成員函數聲明:

            static unsigned hardware_concurrency() noexcept;

    };

std::thread 中主要聲明三類函數:(1). 構造函數、拷貝構造函數及析構函數;(2). 成員函數;(3). 靜态成員函數。另外,std::thread::id 表示線程 ID,同時 C++11 聲明如下:

    class thread::id {

            id() noexcept;

    bool operator==(thread::id x, thread::id y) noexcept;

    bool operator!=(thread::id x, thread::id y) noexcept;

    bool operator&lt;(thread::id x, thread::id y) noexcept;

    bool operator&lt;=(thread::id x, thread::id y) noexcept;

    bool operator&gt;(thread::id x, thread::id y) noexcept;

    bool operator&gt;=(thread::id x, thread::id y) noexcept;

    template&lt;class charT, class traits&gt;

    basic_ostream&lt;charT, traits&gt;&amp;

        operator&lt;&lt; (basic_ostream&lt;charT, traits&gt;&amp; out, thread::id id);

    // Hash 支援

    template &lt;class T&gt; struct hash;

    template &lt;&gt; struct hash&lt;thread::id&gt;;

std::thread 詳解

std::thread 構造和指派

std::thread 構造函數

預設構造函數 (1) thread() noexcept;

初始化構造函數 (2) template &lt;class Fn, class... Args&gt;

explicit thread(Fn&amp;&amp; fn, Args&amp;&amp;... args);

拷貝構造函數 [deleted] (3) thread(const thread&amp;) = delete;

Move 構造函數 (4) thread(thread&amp;&amp; x) noexcept;

預設構造函數(1),建立一個空的 std::thread 執行對象。

初始化構造函數(2),建立一個 std::thread 對象,該 std::thread 對象可被 joinable,新産生的線程會調用 fn 函數,該函數的參數由 args 給出。

拷貝構造函數(被禁用)(3),意味着 std::thread 對象不可拷貝構造。

Move 構造函數(4),move 構造函數(move 語義是 C++11 新出現的概念,詳見附錄),調用成功之後 x 不代表任何std::thread 執行對象。

線程狀态:

在一個線程的生存期内,可以在多種狀态之間轉換,不同的作業系統可以實作不同的線程模型,定義許多不同的線程狀态,每個狀态還可以包含多個子狀态,但大體來說,如下幾種狀态是通用的:

1)就緒:參與排程,等待被執行,一旦被排程選中,立即開始執行

2)運作:占用CPU,正在運作中

3)休眠:暫不參與排程,等待特定事件發生

4)中止:已經運作完畢,等待回收線程資源

線程環境:

線程存在于程序之中,程序内所有全局資源對于内部每個線程都是可見的。

程序内典型全局資源如下:

1)代碼區:這意味着目前程序空間内所有的可見的函數代碼,對于每個線程來說,也是可見的

2)靜态存儲區:全局變量,靜态空間

3)動态存儲區:堆空間

線程内典型的局部資源:

1)本地棧空間:存放本線程的函數調用棧,函數内部的局部變量等

2)部分寄存器變量:線程下一步要執行代碼的指針偏移量

C++中的thread對象通常來說表達了執行的線程(thread of execution)。我在使用多線程的時候,發現很多情況下都是用join()函數,但是在使用detach的時候效果明顯就是不一樣了。

當thread::detach()函數被調用後,執行的線程從線程對象中被分離,該線程被從主線程分離出去放置到背景執行。已不再被一個線程對象所表達--這是兩個獨立的事情。C++線程對象可以被銷毀,同時OS執行的線程可以繼續。如果程式想要知道執行的線程何時結束,就需要一些其它的機制。join()函數在那個thread對象上不能再被調用,因為它已經不再和一個執行的線程相關聯。沒有thread對象指向該線程而失去了對它的控制,當對象析構時線程會繼續在背景執行,但是當主程式退出時并不能保證線程能執行完。如果沒有良好的控制機制或者這種背景線程比較重要,最好不用detach而應該使用join。

去銷毀一個仍然可以“joinable”的C++線程對象會被認為是一種錯誤。為了銷毀一個C++線程對象,約麼join()函數需要被調用(并結束),要麼detach()函數被調用。如果一個C++線程對象當銷毀時仍然可以被join,異常會被抛出。

mutex:

mutex是用來保證線程同步的,防止不同的線程同時操作同一個共享資料。

示例代碼:

int cnt= 20;

mutex m;

void t1()

{

    while (cnt &gt; 0)

    {

        m.lock();

        if (cnt &gt; 0)

        {

            --cnt;

            cout &lt;&lt; cnt &lt;&lt; endl;

        }

        m.unlock();

void t2()

int main()

    thread th1(t1);

    thread th2(t2);

    th1.join();

    th2.join();

    return 0;

運作結果,cnt是依次遞減的,沒有因為多線程而打亂次序:

lock_guard:

使用lock_guard則相對安全,它是基于作用域的,能夠自解鎖,當該對象建立時,它會像m.lock()一樣獲得互斥鎖,當生命周期結束時,它會自動析構(unlock),不會因為某個線程異常退出而影響其他線程。

int cnt = 20;

        lock_guard&lt;mutex&gt; lockGuard(m);

get_id:

擷取線程 ID,傳回一個類型為 std::thread::id 的對象

示例如下:

#include &lt;iostream&gt;

#include &lt;thread&gt;

#include &lt;chrono&gt;

void foo()

    std::this_thread::sleep_for(std::chrono::seconds(1));

    std::thread t1(foo);

    std::thread::id t1_id = t1.get_id();

    std::thread t2(foo);

    std::thread::id t2_id = t2.get_id();

    std::cout &lt;&lt; "t1‘s id: " &lt;&lt; t1_id &lt;&lt; ‘\n‘;

    std::cout &lt;&lt; "t2‘s id: " &lt;&lt; t2_id &lt;&lt; ‘\n‘;

    t1.join();

    t2.join();

sleep_until: 

線程休眠至某個指定的時刻(time point),該線程才被重新喚醒。

template&lt; class Clock, class Duration &gt;

void sleep_until( const std::chrono::time_point&lt;Clock,Duration&gt;&amp; sleep_time );

sleep_for: 

線程休眠某個指定的時間片(time span),該線程才被重新喚醒,不過由于線程排程等原因,實際休眠時間可能比sleep_duration 所表示的時間片更長。

template&lt; class Rep, class Period &gt;

void sleep_for( const std::chrono::duration&lt;Rep,Period&gt;&amp; sleep_duration );

    std::cout &lt;&lt; "waiter" &lt;&lt; std::endl;

    std::chrono::milliseconds dura( 1000 );

    std::this_thread::sleep_for( dura );

    std::cout &lt;&lt; "Waited 1000 ms\n";

繼續閱讀