c 11下的多執行緒與四種互斥量

2021-08-15 03:19:52 字數 3286 閱讀 1003

c++11引入了thread類以及其他用於多執行緒的類,使得我們可以在vs2013下開心的使用多執行緒,不僅如此,thread類的引入還極大的降低了程式設計本身的難度,**簡短移動,便於閱讀。這裡就對c++11下四種互斥量進行解釋和簡單的使用。首先,互斥量存在的意義。舉個簡單的例子,加入大家在畫一幅畫,而只有彩繪工具,如果只有乙個人在畫,雖然效率低一些但是肯定不會出現什麼衝突的現象。如果我們想提高效率,好吧,我們僱5個人同時來寫,效率理論上會提高,但是某工甲想用刷子了,結果刷子被某工乙拿去用了,這時乙不知道該等著還是會去工作,這就出現了衝突。這個問題體現在**中,加入幾個執行緒都可能會操作某靜態全域性變數num,如果不對大家進行協調,程序1在訪問num的時候,程序2也跑來訪問,問題就產生了,程序2的訪問很可能就變得無效了。因此必須某個中立的傢伙,協調大家工作,對於剛才程序1,2都來訪問num的問題,這個中立方即可以規定程序2訪問失敗,直接終止訪問,繼續執行程序2剩餘的部分;也可以規定程序2在此無上限等待程序1訪問num結束,直至程序2成功訪問num;也可以規定乙個時間段,在此時間內程序2等待程序1訪問完num則程序2訪問num,超出時間則程序2停止訪問,繼續執行程序剩餘的部分。顯然,這麼複雜的工作,必然有人幫我們完成了,那就是互斥量。互斥量,顧名思義,把大家的行為分開,在邏輯上是如何實現的呢?互斥量並不是繫結某個變數,類似於看門狗一樣看著他,一次只能有乙個程序進入其中進行訪問,相反,

它實現的邏輯更類似於多個紅綠燈,同乙個互斥量分布在程式的某些角落(類似於斷點),對於同乙個互斥量,一旦在某處被鎖定,也就是亮了綠燈,則在此綠燈期間,其他所有的其他所有該互斥量都亮紅燈,觸發綠燈的程序可以繼續執行,其他程序如果進行到有該紅綠燈(同一互斥量)的位置,都將停止。顯然對於上述程序1,程序2訪問num的狀況,只需要在每個訪問num的語句前設定同乙個互斥量即可,有(第)乙個程序進行訪問,則亮綠燈同行,其他該互斥量全部紅燈,直到第乙個程序訪問完,其他程序才可以訪問(具體情況看互斥量的型別和具體設定)。

下面來看看c++11給我們提供了哪些互斥量。互斥量一共有4種,是四個類:

std::mutex基本互斥量,

std::recursive_mutex遞迴互斥量(可以重複上鎖),

std::time_mutex(可以規定某個時間段內嘗試上鎖),std::recursive_timed_mutex(集上二者大成,可以在某時間段內重複上鎖)。 顯然,上述幾類都被定義在命名空間std中的普通類,不是類模板。

1.std::mutex 基本互斥量

std::mutex是std::_mutex_base的派生類,其方法與之相同如下,成員函式:

void lock():當前執行緒擁有該互斥量,或者說鎖住該互斥量。此時同一mutex類物件呼叫lock函式都將被拒絕,並且對於std::mutex類,不允許同一程序重複鎖住互斥量。像下面的操作是不行滴,這會出現死鎖的現象,直接報錯。

#includestd::mutex m1;

void fun()

void unlock():解除程序對互斥量的鎖定,與上面的lock()對應,很好理解,執行完操作後就解鎖。

bool try_lock():嘗試鎖定,如果鎖定成功,返回true,鎖定失敗,返回false。這樣的設定意圖就顯而易見了,既可以利用if和try_lock()結合實現「嘗試鎖定一次,失敗便繼續執行」的邏輯,也可以用while和try_lock的結合實現「始終嘗試鎖定,直至成功」的邏輯。**如下:

#include#include#include#includestatic int count_ = 0;

std::mutex mtx;

void fun() }}

int main()

for (auto& i : threads)

std::cout << "count:" << count_ << std::endl;

}

顯然,上面的**中十個程序都嘗試修改同乙個靜態全域性變數,每個程序重複一萬次迴圈,每次迴圈+1,看起來最後的結果應該是十萬,但實際結果如下:

原因很簡單,每次上鎖我們都用的是try_lock(),上鎖失敗也不會在這等著,直接跳出if語句,結束本次迴圈。所以最後的結果差不多也就是乙個程序需改後的count_的值,多一些是因為偶爾在沒有被鎖定的時候,其他程序穿插進來偶爾加1。如果想出現十萬的結果,只需要把fun函式的迴圈體做一下小小的修改如下:

void fun()

}

這樣就可以正確的現實結果了,因為上鎖失敗後,程序會一直等在互斥量上鎖的這個位置,直到鎖被其他位置的互斥量開啟,上鎖成功,程序才會繼續。結果如下:

2.std::recursive_mutex 遞迴互斥量

這個遞迴互斥量的特點就是,允許同一程序多次對某互斥量重複上鎖,而不會出現死鎖。但是上鎖次數和開鎖次數必須吻合,執行緒才會真正的解鎖這個互斥量。這就類似於遞迴的呼叫某函式,進入函式退出函式的次數必須相同,入棧出棧的次數也必須相同,才會回到最初的狀態,因此美名曰遞迴互斥量(個人理解,歡迎拍磚)。

這個類的方法和基本互斥量是一樣的,唯一的區別就在於可以重複上鎖,下面舉乙個簡單的例子:

#include#include#include#includestatic int count_ = 0;

std::recursive_mutex rmtx;

void fun_1()

void fun_2()

std::cout << "fun_2 lock" << std::endl;

rmtx.unlock();

}void fun_3()

int main()

輸出的結果如下:

注意列印前四行之間的間隔都是三秒,最後兩行同時被列印,說明當執行緒1的3個鎖都被解鎖之後(每三秒解鎖一次),其他執行緒瞬間可以對互斥量上鎖,然後進行操作。

C 11多執行緒使用互斥變數

在學習作業系統的時候,有學過互斥變數,也就是用來保護原子數在同一時刻只能被乙個執行緒進行訪問和修改。c 中通過例項化 std mutex 建立互斥量,通過呼叫成員函式lock 進行上鎖,unlock 進行解鎖。不過,不推薦實踐中直接去呼叫成員函式,因為呼叫成員函式就意味著,必須記住在每個函式出口都要...

C 11 多執行緒與鎖

多執行緒是小型軟體開發必然的趨勢。c 11將多執行緒相關操作全部整合到標準庫中了,省去了某些坑庫的編譯,真是大大的方便了軟體開發。多執行緒這個庫簡單方便實用,下面給出簡單的例子 include include include using namespace std volatile int val ...

C 11 多執行緒同步 互斥鎖 條件變數

在多執行緒程式中,執行緒同步 多個執行緒訪問乙個資源保證順序 是乙個非常重要的問題,linux下常見的執行緒同步的方法有下面幾種 這篇部落格只介紹互斥量和條件變數的使用。通常情況下,互斥鎖和條件變數是配合使用的,互斥鎖用於短期鎖定,主要保證執行緒對臨界區的進入 條件變數用於執行緒長期等待,在wait...