<menu id="w8yyk"><menu id="w8yyk"></menu></menu>
  • <dd id="w8yyk"><nav id="w8yyk"></nav></dd>
    <menu id="w8yyk"></menu>
    <menu id="w8yyk"><code id="w8yyk"></code></menu>
    <menu id="w8yyk"></menu>
    <xmp id="w8yyk">
    <xmp id="w8yyk"><nav id="w8yyk"></nav>
  • 網站首頁 > 物聯資訊 > 技術分享

    漫話C++0x(五)―- thread, mutex, condition_variable

    2016-09-28 00:00:00 廣州睿豐德信息科技有限公司 閱讀
    睿豐德科技 專注RFID識別技術和條碼識別技術與管理軟件的集成項目。質量追溯系統、MES系統、金蝶與條碼系統對接、用友與條碼系統對接

     熟悉C++98的朋友,應該都知道,在C++98中沒有thread, mutex, condition_variable這些與concurrency相關的特性支持,如果需要寫多線程相關程序,都要借助于不同平臺上各自提供的api,這樣帶來的問題就是程序的跨平臺移植性比較差,經常要用一大堆的#ifdef WIN32類似的宏來區分不同的平臺,搞得程序很難看。C++0x最原始的初衷之一就是為了讓C++的功能更加強大,更加方便使用。現如今硬件如此發達,concurrency在程序設計中已經是司空見慣的事情了,如果C++再不支持這些concurrency相關的特性,就真的out了。現在,C++程序員的福音到了,C++0x提供了對thread, mutex, condition_variable這些concurrency相關特性的支持,以后多線程這一塊的代碼可以完全跨平臺了,而且由于C++0x封裝的都比較好,代碼寫起來也十分簡潔。下面開始介紹今天的內容。

      • 1. thread

        寫過多線程程序的朋友,相信對thread本身都不會陌生,這里不對thread本身做太多的說明,以介紹C++0x中提供的thread的用法為主。請大家先看下面的例子:

    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
    
    #include < iostream>
    #include < string>
    #include < thread>
     
    class Printer
    {
    public:
        void Print(int id, std::string& name)
        {   
            std::cout < < "id=" << id << ", name=" << name;
        }   
    };
     
    void Hello()
    {
        std::cout << "hello world" << std::endl;
    }
     
    int main()
    {
        Printer p;
        int id = 1;
        std::string name("xiao5ge");
     
        std::thread t1(&Printer::Print, p, id, name);
        std::thread t2(std::bind(&Printer::Print, p, id, name));
        std::thread t3([&]{ p.Print(id, name); }); 
        std::thread t4(Hello);
     
        t4.join();
        t3.join();
        t2.join();
        t1.join();
    }

        下面我們來通過分析上面的例子,來說明一下thread的用法。上面的t1-t4的四個例子,分別是thread的四種構造方式,我們一一來介紹一下:

        • (1)這種方式是通過變參數模板實現的,第一個參數是線程入口函數的地址,后面的參數按函數調用時的參數順序傳入。這里需要說明兩點:一是變參模板也是C++0x新增的特性,將會在后面的文章中介紹;二是,類成員函數的第一個參數永遠是this,所以這里第一個參數放的是對象本身。
        • (2)這種方式是傳入一個std::function對象,bind/function在前一篇中有介紹,不熟悉的朋友可以先看一下C++0x系列的第四篇。
        • (3)這種方式是傳入一個lambda表達式,也即一個closure,是比較常用的方式。關于lambda也在上一篇中有介紹。
        • (4)這種方式是最簡單,最常用的方式,直接傳入一個函數,寫過多線程程序的朋友應該對這種方式最為熟悉。

        上面介紹了C++0x thread的基本用法,下面需要再補充幾點使用過程需要注意的事項:

        • (1)如果入口函數的參數是以引用或指針形式傳入的,需要使用者保證在線程運行過程中,這些參數一直是有效的。同時,如果有多個線程會訪問或者修改這些變量,需要使用者做好同步,保證一致性。
        • (2)關于上面提到的幾種構造方式,簡單的直接用4,復雜的推薦的選擇順序:1->3->2,即變參模板方式->lambda方式->bind方式
        • (3)通常需要等子線程運行完,主線程才退出,所以在主線程中通常需要調各子線程的join()。
      • 2. mutex

        mutex實現的是“互斥鎖”的語義,在多線程的程序中,經常需要通過鎖的機制來保證數據的一致性,C++0x提供了下面四種語義的mutex:

        • (1) std::mutex: 普通的互斥鎖,不能遞歸使用
        • (2) std::timed_mutex:帶超時的互斥鎖,不能遞歸使用
        • (3) std::recursive_mutex:遞歸互斥鎖
        • (3) std::recursive_timed_mutex:帶超時的遞歸互斥鎖

        關于mutex的使用,我們通常建議使用RAII(Resource Acquisition is Initialization)的方式,即在構造的時候lock, 析構的時候unlock, 不建議直接顯式的lock/unlock,因為這樣比較容易出錯。因此,C++0x也提供了兩個工具類std::lock_guard和std::unique_lock來輔助我們使用mutex,下面我們通過例子來看一下具體的使用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
    #include < mutex>
     
    // global vars
    int data = 0;
    std::mutex data_mutex;
     
    // thread 1
    {
        std::lock_guard< std::mutex> locker(data_mutex);
        data = 1;
    }
     
    // thread 2
    {
        std::lock_guard< std::mutex> locker(data_mutex);
        data = 2;
    }

        從上面的例子,相信大家可以對mutex的基本使用方法都應該比較清楚了,由于mutex本身就比較簡單,這里不再贅言。說一下std::unique_lock和std::lock_guard的區別,std::lock_guard只允許RAII方式的使用,而std::unique_lock可以在構造之后調用lock/unlock, 更加靈活一些,但使用的時候出錯的機率也更大一些,所以如果沒有什么特殊的需求,通常推薦盡量使用std::lock_guard.

      • 3. condition_variable

        關于condition_variable,它的語義是今天講的三個內容里面相對復雜一些的,我在之前也寫過一篇關于它的文章,不熟悉的朋友可以先閱讀一下《條件變量(Condition Variable)詳解》這篇文章,先了解一下條件變量,以方便理解后面的內容。我們知道,條件變量主要是用在多線程之間修改了shared_data之后的相互通信,由于條件變量在多線程編程中非常有用,所以C++0x也添加了對條件變量的支持,下面是C++0x提供的兩種不同類型的條件變量:

    • (1)condition_variable: 用在std::unique_lock< std::mutex>上wait, 比較高效。
    • (2)condition_variable_any: 可以用在任意mutex上wait, 比較靈活,但效率比condition_variable差一些。

        下面我們通過例子來看看,條件變量在C++0x中的使用方式:

    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
    
    // global
    std::atomic< bool> is_finish(false);
    std::mutex finish_mutex;
    std::condition_variable finish_cond;
     
    // thread 1
    {
        std::unique< std::mutex> locker(finish_mutex);
     
        // 1. loop wait
        while (!is_finish)
        {   
            finish_cond.wait(locker);
        }   
     
        // 2. wait until prediction is true, loop inside
        finish_cond.wait(locker, []{ return is_finish; }); 
     
        // 3. wait until eithor prediction is true or timeout
        finish_cond.wait(locker, std::chrono::seconds(1),
                []{ return is_finish; }); 
    }
     
    // thread 2
    {
        is_finish = true;
     
        // 1. notify one of the waiter
        finish_cond.notify_one();
     
        // 2. notify all the waiter
        finish_cond.notify_all();
    }

        上面的例子,基本覆蓋了C++0x提供的條件變量的主要用法。下面我們來一一分析一下,幫助大家更好的理解:

    • (1)關于wait,有三種基本的用法:第1種是在指定的條件上循環等待,直到條件為真notify時才會繼續執行后面的邏輯;第2種用法語義上和第1種是一樣的,但是不是用戶做顯式的loop等待,用戶傳入一個需要滿足的條件的closure, wait一直等到這個條件為真被notify時才會返回繼續執行下面的邏輯,可以理解為這時候,在wait內部有一個loop;第3 種用法,加了一個超時的語義,wait一直等到條件為真或者超時,被notify時才會返回繼續執行下面的邏輯。
    • (2)關于notify, 有兩種:第1種是notify_one, 只喚醒一個在wait的線程; 第2種是notify_all,喚醒所有在wait的線程,相當于一個broadcast的語義。

        以上即是今天的主要內容,希望對正在學習C++0x的朋友有所幫助,榮幸之至!

    RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成
    最近免费观看高清韩国日本大全