熟悉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的用法為主。請大家先看下面的例子:
下面我們來通過分析上面的例子,來說明一下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,下面我們通過例子來看一下具體的使用:
從上面的例子,相信大家可以對mutex的基本使用方法都應該比較清楚了,由于mutex本身就比較簡單,這裡不再贅言。說一下std::unique_lock和std::lock_guard的差別,std::lock_guard隻允許RAII方式的使用,而std::unique_lock可以在構造之後調用lock/unlock, 更加靈活一些,但使用的時候出錯的機率也更大一些,是以如果沒有什麼特殊的需求,通常推薦盡量使用std::lock_guard.
3. condition_variable
(1)condition_variable: 用在std::unique_lock< std::mutex>上wait, 比較高效。
(2)condition_variable_any: 可以用在任意mutex上wait, 比較靈活,但效率比condition_variable差一些。
下面我們通過例子來看看,條件變量在C++0x中的使用方式:
上面的例子,基本覆寫了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的朋友有所幫助,榮幸之至!