C++線程中幾類鎖的詳解
C++線程中的幾類鎖
多線程中的鎖主要有五類:互斥鎖、條件鎖、自旋鎖、讀寫鎖、遞歸鎖。一般而言,所得功能與性能成反比。而且我們一般不使用遞歸鎖(C++提供std::recursive_mutex),這里不做介紹。
互斥鎖
==互斥鎖用于控制多個(gè)線程對(duì)它們之間共享資源互斥訪問的一個(gè)信號(hào)量。==也就是說為了避免多個(gè)線程在某一時(shí)刻同時(shí)操作一個(gè)共享資源,例如一個(gè)全局變量,任何一個(gè)線程都要使用初始鎖互斥地訪問,以避免多個(gè)線程同時(shí)訪問發(fā)生錯(cuò)亂。
在某一時(shí)刻只有一個(gè)線程可以獲得互斥鎖,在釋放互斥鎖之前其它線程都不能獲得互斥鎖,以阻塞的狀態(tài)在一個(gè)等待隊(duì)列中等待。
頭文件:#include
類型:std::std::mutex、std::lock_guard
用法:在C++中,通過構(gòu)造std::mutex的實(shí)例創(chuàng)建互斥單元,調(diào)用成員函數(shù)lock()來鎖定共享資源,調(diào)用unlock()來解鎖。不過一般不使用這種解決方案,更多的是使用C++標(biāo)準(zhǔn)庫(kù)中的std::lock_guard類模板,實(shí)現(xiàn)了一個(gè)互斥量包裝程序,提供了一種方便的RAII風(fēng)格的機(jī)制在作用域塊中。
關(guān)于RAII慣用法的介紹:。。。
示例代碼:
#include <iostream> #include <thread>//C++11線程庫(kù)是跨平臺(tái)的 #include <mutex>//C++互斥鎖 #include <vector> #include <windows.h> int g_num = 0; std::mutex g_mutex; void ThreadFunc(int a) { cout << "啟動(dòng)線程:" << a << endl; for (int i = 0; i < 1000000; i++) { //g_mutex.lock(); std::lock_guard<std::mutex> m(g_mutex);//互斥量包裝程序 g_num++; //g_mutex.unlock(); } } int main() { for (int i = 0; i < 4; i++) { std::thread t(ThreadFunc, i); t.detach(); } Sleep(2000); cout << "g_num:" << g_num << endl; return 0; } //高階版,將上述main()函數(shù)的函數(shù)名更改,再更改以下的mainTest()即可執(zhí)行。兩個(gè)方法的執(zhí)行的結(jié)果相同,原理也相同。 int mainTest() { std::vector<std::thread *> ts; for (int i = 0; i < 4; i++) { std::thread *t = new std::thread(ThreadFunc, i); //t.detach(); ts.push_back(t); } for (auto begin = ts.begin(); begin != ts.end(); begin++) (*begin)->join(); Sleep(2000); cout << "g_num:" << g_num << endl; return 0; }
TIPS:注意std::cout和std::end都是線程不安全的,所以才會(huì)出現(xiàn)線程1和線程3在一行,原因就是線程1未執(zhí)行cout<<endl。CPU的時(shí)間片就已經(jīng)用完了,CPU轉(zhuǎn)移執(zhí)行線程3后,再執(zhí)行線程1的cout<<endl。
具體C++11中thread庫(kù)join和detach的區(qū)別可參考:http://chabaoo.cn/article/229636.htm
條件鎖
條件鎖就是所謂的條件變量,當(dāng)某一個(gè)線程因?yàn)槟硞€(gè)條件未滿足時(shí)可以使用條件變量使該程序處于阻塞狀態(tài),一旦條件滿足則以“信號(hào)量”的方式喚醒一個(gè)因?yàn)樵摋l件而被阻塞的線程。最為常見的就是再線程池中,初始情況下因?yàn)闆]有任務(wù)使得任務(wù)隊(duì)列為空,此時(shí)線程池中的線程因?yàn)椤叭蝿?wù)隊(duì)列為空”這個(gè)條件處于阻塞狀態(tài)。一旦有任務(wù)進(jìn)來,就會(huì)以信號(hào)量的方式喚醒該線程來處理這個(gè)任務(wù)。
自旋鎖
互斥鎖和條件鎖都是比較常見的鎖,比較容易理解。接下來用互斥鎖和自旋鎖的原理相互比較,來理解自旋鎖。
假設(shè)我們有一臺(tái)計(jì)算機(jī),該計(jì)算機(jī)擁有兩個(gè)處理器core1和core2.現(xiàn)在在這臺(tái)計(jì)算機(jī)上運(yùn)行兩個(gè)線程:T1和T2,且T1和T2分別在處理器core1和core2上面運(yùn)行,兩個(gè)線程之間共享一份公共資源Public。
首先我們說明互斥鎖的工作原理,互斥鎖是一種sleep-waiting的鎖。假設(shè)線程T1訪問公共資源Public并獲得互斥鎖,同時(shí)在core1處理器上運(yùn)行,此時(shí)線程T2也想要訪問這份公共資源Public(即想要獲得互斥鎖),但是由于T1正在使用Public使得T2被阻塞。當(dāng)T2處于阻塞狀態(tài)時(shí),T2被放入等待隊(duì)列中,處理器core2會(huì)去處理其它的任務(wù)而不必一直等待(忙等)。也就是說處理器不會(huì)因?yàn)榫€程被阻塞而空閑,它會(huì)去處理其它事務(wù)。
然后我們說明自旋鎖的工作原理,自旋鎖是一種busy-waiting的鎖。也就是說,如果T1正在使用Public,而T2也想使用Public,此時(shí)T2肯定是得不到這個(gè)自旋鎖的。與互斥鎖相反,此時(shí)運(yùn)行T2的處理器core2會(huì)一直不斷地循環(huán)檢查Public使用可用(自旋鎖請(qǐng)求),直到獲得到這個(gè)自旋鎖為止。
從“自旋鎖”的名稱也可以看出,如果一個(gè)線程想要獲得一個(gè)被使用的自旋鎖,那么它會(huì)一直占用CPU請(qǐng)求這個(gè)自旋鎖使得CPU不能去做其它的事情,知道獲取這個(gè)鎖為止,這就是“自旋”的含義。當(dāng)發(fā)生阻塞時(shí),互斥鎖可以讓CPU去處理其它的事務(wù),但自旋鎖讓CPU一直不斷循環(huán)請(qǐng)求獲取這個(gè)鎖。通過比較,我們可以明顯的得出結(jié)論:“自旋鎖”是比較消耗CPU的。
讀寫鎖
讀寫鎖我們可以借助于“讀者-寫者”問題進(jìn)行理解。接下來我們簡(jiǎn)單說下“讀者-寫者”問題。
計(jì)算機(jī)中某些數(shù)據(jù)被多個(gè)進(jìn)程共享,對(duì)數(shù)據(jù)庫(kù)的操作有兩種:一種是讀操作,就是從數(shù)據(jù)庫(kù)中讀取數(shù)據(jù)不會(huì)修改數(shù)據(jù)庫(kù)中內(nèi)容;另一種就是寫操作,寫操作會(huì)修改數(shù)據(jù)庫(kù)中存放的數(shù)據(jù)。因此可以得到我們?cè)试S在數(shù)據(jù)庫(kù)上同時(shí)執(zhí)行多個(gè)“讀”操作,但是某一時(shí)刻只能在數(shù)據(jù)庫(kù)上有一個(gè)“寫”操作來更新數(shù)據(jù)。這就是簡(jiǎn)單的讀者-寫者模型。
參考博客
http://chabaoo.cn/article/214502.htm
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
C語言全部?jī)?nèi)存操作函數(shù)的實(shí)現(xiàn)詳細(xì)講解
這篇文章主要介紹了C語言全部?jī)?nèi)存操作函數(shù)的實(shí)現(xiàn)詳細(xì)講解,作者用圖文代碼實(shí)例講解的很清晰,有感興趣的同學(xué)可以研究下2021-02-02使用C語言訪問51單片機(jī)中存儲(chǔ)器的核心代碼
這篇文章主要介紹了使用C語言訪問51單片機(jī)中存儲(chǔ)器的相關(guān)知識(shí),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-01-01C語言實(shí)現(xiàn)游戲VIP停車場(chǎng)管理系統(tǒng)
這篇文章主要介紹了C語言實(shí)現(xiàn)游戲VIP停車場(chǎng)管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-12-12