C++——互斥量
文章目錄
- 一、基本知識(shí)
- 二、獨(dú)占互斥量mutex
- 1.mutex的介紹
- 2.mutex的成員函數(shù)
- 3.實(shí)例演示
- 三、lock_guard和unique_lock的使用和區(qū)別
- 四、遞歸互斥量recursive_mutex
- 1.基本知識(shí)
- 2.演示示例
- 五、帶超時(shí)的互斥量std::timed_mutex和std::recursive_timed_mutex
一、基本知識(shí)
C++11提供如下4種語義的互斥量(mutex)
std::mutex,獨(dú)占的互斥量,不能遞歸使用。
std::time_mutex,帶超時(shí)的獨(dú)占互斥量,不能遞歸使用。
std::recursive_mutex,遞歸互斥量,不帶超時(shí)功能。
std::recursive_timed_mutex,帶超時(shí)的遞歸互斥量。
使用時(shí)需要包含頭文件
二、獨(dú)占互斥量mutex
1.mutex的介紹
下面以 std::mutex 為例介紹 C++11 中的互斥量用法。
std::mutex 是C++11 中最基本的互斥量,std::mutex 對象提供了獨(dú)占所有權(quán)的特性——即不支持遞歸地對 std::mutex 對象上鎖,而 std::recursive_lock 則可以遞歸地對互斥量對象上鎖。
2.mutex的成員函數(shù)
構(gòu)造函數(shù):std::mutex不允許拷貝構(gòu)造,也不允許 move 拷貝,最初產(chǎn)生的 mutex 對象是處于unlocked 狀態(tài)的。
lock():調(diào)用線程將鎖住該互斥量。線程調(diào)用該函數(shù)會(huì)發(fā)生下面 3 種情況:
①如果該互斥量當(dāng)前沒有被鎖住,則調(diào)用線程將該互斥量鎖住,直到調(diào)用 unlock之前,該線程一直擁有該鎖。
② 如果當(dāng)前互斥量被其他線程鎖住,則當(dāng)前的調(diào)用線程被阻塞住。
③如果當(dāng)前互斥量被當(dāng)前調(diào)用線程鎖住,則會(huì)產(chǎn)生死鎖。
unlock(): 解鎖,釋放對互斥量的所有權(quán)。
try_lock():嘗試鎖住互斥量,如果互斥量被其他線程占有,則當(dāng)前線程也不會(huì)被阻塞。線程調(diào)用該函數(shù)也會(huì)出現(xiàn)下面 3 種情況:這些
①如果當(dāng)前互斥量沒有被其他線程占有,則該線程鎖住互斥量,直到該線程調(diào)用 unlock 釋放互斥量。
②如果當(dāng)前互斥量被其他線程鎖住,則當(dāng)前調(diào)用線程返回
false,而并不會(huì)被阻塞掉。
③如果當(dāng)前互斥量被當(dāng)前調(diào)用線程鎖住,則會(huì)產(chǎn)生死鎖。
3.實(shí)例演示
#include<iostream> #include<mutex> #include<thread> using namespace std;std::mutex mtx; int counter;void pthread_fun() {for (int i = 0; i < 10000; i++){mtx.lock();counter++;mtx.unlock();} }int main() {thread pthread[10];for (int i = 0; i < 10; i++){pthread[i] = thread(pthread_fun);}for (int i = 0; i < 10; i++){pthread[i].join();}cout << counter << endl;return 0; }運(yùn)行結(jié)果:
三、lock_guard和unique_lock的使用和區(qū)別
相對于手動(dòng)lock和unlock,我們可以使用RAII(通過類的構(gòu)造析構(gòu))來實(shí)現(xiàn)更好的編碼方式。
這里涉及到unique_lock,lock_guard的使用。
C++相較于C引入了很多新的特性, 比如可以在代碼中拋出異常, 如果還是按照以前的加鎖解鎖的話代碼會(huì)極為復(fù)雜繁瑣。
unique_lock,lock_guard的區(qū)別:
unique_lock與lock_guard都能實(shí)現(xiàn)自動(dòng)加鎖和解鎖,但是前者更加靈活,能實(shí)現(xiàn)更多的功能。
unique_lock可以進(jìn)行臨時(shí)解鎖和再上鎖,如在構(gòu)造對象之后使用lck.unlock()就可以進(jìn)行解鎖,lck.lock()進(jìn)行上鎖,而不必等到析構(gòu)時(shí)自動(dòng)解鎖。
對于locak_guard來說,unique_lock顯得比較靈活一點(diǎn),因?yàn)樵趌ock_guard和unique_lock都可以借用對象有生存期調(diào)用析構(gòu)函數(shù)釋放鎖的情況下,unique_lock可以還自己上鎖釋放鎖,顯得比較靈活一點(diǎn),所以平時(shí)比較經(jīng)常使用unique_lock。
四、遞歸互斥量recursive_mutex
1.基本知識(shí)
遞歸鎖允許同一個(gè)線程多次獲取該互斥鎖,可以用來解決同一線程需要多次獲取互斥量時(shí)死鎖的問題。
雖然遞歸鎖能解決這種情況的死鎖問題,但是盡量不要使用遞歸鎖,主要原因如下:
(1)需要用到遞歸鎖的多線程互斥處理本身就是可以簡化的,允許遞歸很容易放縱復(fù)雜邏輯的產(chǎn)生,并且產(chǎn)生晦澀,當(dāng)要使用遞歸鎖的時(shí)候應(yīng)該重新審視自己的代碼是否一定要使用遞歸鎖;
(2)遞歸鎖比起非遞歸鎖,效率會(huì)低;
(3)遞歸鎖雖然允許同一個(gè)線程多次獲得同一個(gè)互斥量,但可重復(fù)獲得的最大次數(shù)并未具體說明,一旦超過一定的次數(shù),再對lock進(jìn)行調(diào)用就會(huì)拋出std::system錯(cuò)誤。
2.演示示例
#include<iostream> #include<mutex> #include<thread> using namespace std;std::recursive_mutex rc_mtx;//可遞歸的鎖 std::mutex mtx; int g_val = 0;void fun(int&& x) {if (x == 0) return;std::unique_lock<std::recursive_mutex>lck(rc_mtx);//right//下面語句error 會(huì)造成死鎖,因?yàn)檫f歸的時(shí)候鎖沒有釋放,再次獲得鎖,造成死鎖。//std::unique_lock<std::mutex>lck(mtx);g_val++;x--;fun(std::move(x)); }int main() {std::thread pthreads[5];for (int i = 0; i < 5; i++){pthreads[i] = std::thread(fun, 5);}for (int i = 0; i < 5; i++){pthreads[i].join();}cout << g_val << endl;return 0; }運(yùn)行結(jié)果:
五、帶超時(shí)的互斥量std::timed_mutex和std::recursive_timed_mutex
std::timed_mutex比std::mutex多了兩個(gè)超時(shí)獲取鎖的接口:
try_lock_for:獲取鎖的時(shí)間,過了這個(gè)時(shí)間會(huì)釋放鎖
try_lock_until:嘗試獲得鎖,直到過了某個(gè)時(shí)間,返回false。
總結(jié)
- 上一篇: C++——右值引用
- 下一篇: 计算机操作系统——死锁