C++——互斥量
文章目錄
- 一、基本知識
- 二、獨占互斥量mutex
- 1.mutex的介紹
- 2.mutex的成員函數
- 3.實例演示
- 三、lock_guard和unique_lock的使用和區別
- 四、遞歸互斥量recursive_mutex
- 1.基本知識
- 2.演示示例
- 五、帶超時的互斥量std::timed_mutex和std::recursive_timed_mutex
一、基本知識
C++11提供如下4種語義的互斥量(mutex)
std::mutex,獨占的互斥量,不能遞歸使用。
std::time_mutex,帶超時的獨占互斥量,不能遞歸使用。
std::recursive_mutex,遞歸互斥量,不帶超時功能。
std::recursive_timed_mutex,帶超時的遞歸互斥量。
使用時需要包含頭文件
二、獨占互斥量mutex
1.mutex的介紹
下面以 std::mutex 為例介紹 C++11 中的互斥量用法。
std::mutex 是C++11 中最基本的互斥量,std::mutex 對象提供了獨占所有權的特性——即不支持遞歸地對 std::mutex 對象上鎖,而 std::recursive_lock 則可以遞歸地對互斥量對象上鎖。
2.mutex的成員函數
構造函數:std::mutex不允許拷貝構造,也不允許 move 拷貝,最初產生的 mutex 對象是處于unlocked 狀態的。
lock():調用線程將鎖住該互斥量。線程調用該函數會發生下面 3 種情況:
①如果該互斥量當前沒有被鎖住,則調用線程將該互斥量鎖住,直到調用 unlock之前,該線程一直擁有該鎖。
② 如果當前互斥量被其他線程鎖住,則當前的調用線程被阻塞住。
③如果當前互斥量被當前調用線程鎖住,則會產生死鎖。
unlock(): 解鎖,釋放對互斥量的所有權。
try_lock():嘗試鎖住互斥量,如果互斥量被其他線程占有,則當前線程也不會被阻塞。線程調用該函數也會出現下面 3 種情況:這些
①如果當前互斥量沒有被其他線程占有,則該線程鎖住互斥量,直到該線程調用 unlock 釋放互斥量。
②如果當前互斥量被其他線程鎖住,則當前調用線程返回
false,而并不會被阻塞掉。
③如果當前互斥量被當前調用線程鎖住,則會產生死鎖。
3.實例演示
#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; }運行結果:
三、lock_guard和unique_lock的使用和區別
相對于手動lock和unlock,我們可以使用RAII(通過類的構造析構)來實現更好的編碼方式。
這里涉及到unique_lock,lock_guard的使用。
C++相較于C引入了很多新的特性, 比如可以在代碼中拋出異常, 如果還是按照以前的加鎖解鎖的話代碼會極為復雜繁瑣。
unique_lock,lock_guard的區別:
unique_lock與lock_guard都能實現自動加鎖和解鎖,但是前者更加靈活,能實現更多的功能。
unique_lock可以進行臨時解鎖和再上鎖,如在構造對象之后使用lck.unlock()就可以進行解鎖,lck.lock()進行上鎖,而不必等到析構時自動解鎖。
對于locak_guard來說,unique_lock顯得比較靈活一點,因為在lock_guard和unique_lock都可以借用對象有生存期調用析構函數釋放鎖的情況下,unique_lock可以還自己上鎖釋放鎖,顯得比較靈活一點,所以平時比較經常使用unique_lock。
四、遞歸互斥量recursive_mutex
1.基本知識
遞歸鎖允許同一個線程多次獲取該互斥鎖,可以用來解決同一線程需要多次獲取互斥量時死鎖的問題。
雖然遞歸鎖能解決這種情況的死鎖問題,但是盡量不要使用遞歸鎖,主要原因如下:
(1)需要用到遞歸鎖的多線程互斥處理本身就是可以簡化的,允許遞歸很容易放縱復雜邏輯的產生,并且產生晦澀,當要使用遞歸鎖的時候應該重新審視自己的代碼是否一定要使用遞歸鎖;
(2)遞歸鎖比起非遞歸鎖,效率會低;
(3)遞歸鎖雖然允許同一個線程多次獲得同一個互斥量,但可重復獲得的最大次數并未具體說明,一旦超過一定的次數,再對lock進行調用就會拋出std::system錯誤。
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 會造成死鎖,因為遞歸的時候鎖沒有釋放,再次獲得鎖,造成死鎖。//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; }運行結果:
五、帶超時的互斥量std::timed_mutex和std::recursive_timed_mutex
std::timed_mutex比std::mutex多了兩個超時獲取鎖的接口:
try_lock_for:獲取鎖的時間,過了這個時間會釋放鎖
try_lock_until:嘗試獲得鎖,直到過了某個時間,返回false。
總結
- 上一篇: C++——右值引用
- 下一篇: C++——异步操作(std::futur