C++11互斥量的具體使用
互斥量是一種同步原語(yǔ),是一種線程同步的手段,用來(lái)保護(hù)多線程同時(shí)訪問(wèn)的共享數(shù)據(jù)。
C++11中提供了如下4種語(yǔ)義的互斥量(mutex):
1、std::mutex:獨(dú)占的互斥量,不能遞歸使用。
2、std::mutex_mutex:帶超時(shí)的獨(dú)占互斥量,不能遞歸使用。
3、std::recursive_mutex:遞歸互斥量,不帶超時(shí)功能。
4、std::recursive_timed_mutex:帶超時(shí)的遞歸互斥量
獨(dú)占互斥量std::mutex
這些互斥量的基本接口很相似,一般用法是通過(guò)lock()方法來(lái)阻塞線程,直到獲得互斥量的所有權(quán)為止。在線程獲得互斥量并完成任務(wù)之后,就必須使用unlock()來(lái)解除對(duì)互斥量的占用,lock()和unlock()必須成對(duì)出現(xiàn)。try_lock()嘗試鎖定互斥量,如果成功則返回true,如果失敗則返回false,它是阻塞的。std::mutex的基本用法如下代碼。
#include <iostream> #include <thread> #include <mutex> #include <chrono> using namespace std; std::mutex g_lock; void func() { g_lock.lock(); cout << "enter thread: " << std::this_thread::get_id() << endl; std::this_thread::sleep_for(std::chrono::seconds(1)); cout << "leaving thread: " << std::this_thread::get_id() << endl; g_lock.unlock(); } ///g++ mutex.cpp -lpthread int main() { std::thread t1(func); std::thread t2(func); std::thread t3(func); t1.join(); t2.join(); t3.join(); return 0; }
輸出結(jié)果如下:
enter thread: 140569127851776
leaving thread: 140569127851776
enter thread: 140568859412224
leaving thread: 140568859412224
enter thread: 140568590972672
leaving thread: 140568590972672
使用lock_guard可以簡(jiǎn)化lock/unlock的寫法,同時(shí)也更安全,因?yàn)閘ock_guard在構(gòu)造函數(shù)時(shí)會(huì)自動(dòng)鎖定互斥量,而在退出作用域后進(jìn)行析構(gòu)時(shí)就會(huì)自動(dòng)解鎖,從而保證了互斥量的正確操作,避免忘記unlock操作,因此,應(yīng)盡量用lock_guard。lock_guard用到了RAII技術(shù),這種技術(shù)在類的構(gòu)造函數(shù)中分配資源,在析構(gòu)函數(shù)中釋放資源,保證資源在出了作用域之后就釋放,上面的例子使用lock_guard后更簡(jiǎn)潔,代碼如下:
#include <iostream> #include <thread> #include <mutex> #include <chrono> using namespace std; std::mutex g_lock; void func() { std::lock_guard<std::mutex> locker(g_lock);///出了作用域之后自動(dòng)解鎖 cout << "enter thread: " << std::this_thread::get_id() << endl; std::this_thread::sleep_for(std::chrono::seconds(1)); cout << "leaving thread: " << std::this_thread::get_id() << endl; } ///g++ mutex.cpp -lpthread int main() { std::thread t1(func); std::thread t2(func); std::thread t3(func); t1.join(); t2.join(); t3.join(); return 0; }
遞歸的獨(dú)占互斥量std::recursive_mutex
遞歸鎖允許同一個(gè)線程多次獲得該互斥鎖,可以用來(lái)解決同一個(gè)線程需要多次獲取互斥量死鎖的問(wèn)題。在下面的代碼中,一個(gè)線程多次獲取同一個(gè)互斥量時(shí)會(huì)發(fā)生死鎖。
#include <iostream> #include <thread> #include <mutex> #include <chrono> using namespace std; struct Complex { public: Complex(){i = 20;} void mul(int x) { printf("%s %s %d\n", __FILE__, __func__, __LINE__); g_mutex.lock(); ///std::lock_guard<std::mutex> locker(g_mutex); i *= x; printf("%s %s %d\n", __FILE__, __func__, __LINE__); g_mutex.unlock(); } void div(int x) { printf("%s %s %d\n", __FILE__, __func__, __LINE__); g_mutex.lock(); ///std::lock_guard<std::mutex> locker(g_mutex); i /= x; printf("%s %s %d\n", __FILE__, __func__, __LINE__); g_mutex.unlock(); } void both(int x, int y) { ///std::lock_guard<std::mutex> locker(g_mutex); g_mutex.lock(); printf("%s %s %d\n", __FILE__, __func__, __LINE__); mul(x); div(y); g_mutex.unlock(); printf("%s %s %d\n", __FILE__, __func__, __LINE__); } private: int i; std::mutex g_mutex; }; ///g++ mutex.cpp -lpthread int main() { Complex complex; complex.both(2, 4); return 0; }
這個(gè)例子運(yùn)行起來(lái)就會(huì)發(fā)生死鎖,因?yàn)樵谡{(diào)用both時(shí)獲取了互斥量,之后再調(diào)用mul又要獲取相同的互斥量,但是這個(gè)互斥量已經(jīng)被當(dāng)前線程獲取了,無(wú)法釋放,這時(shí)就會(huì)發(fā)生死鎖。要解決這個(gè)死鎖的問(wèn)題,一個(gè)簡(jiǎn)單的辦法就是用遞歸鎖:std::recursive_mutex,它允許同一個(gè)線程多次獲得互斥量。
#include <iostream> #include <thread> #include <mutex> #include <chrono> using namespace std; struct Complex { public: Complex(){i = 20;} void mul(int x) { printf("%s %s %d\n", __FILE__, __func__, __LINE__); g_mutex.lock(); ///std::lock_guard<std::recursive_mutex> locker(g_mutex); i *= x; printf("%s %s %d\n", __FILE__, __func__, __LINE__); g_mutex.unlock(); } void div(int x) { printf("%s %s %d\n", __FILE__, __func__, __LINE__); g_mutex.lock(); ///std::lock_guard<std::recursive_mutex> locker(g_mutex); i /= x; printf("%s %s %d\n", __FILE__, __func__, __LINE__); g_mutex.unlock(); } void both(int x, int y) { ///std::lock_guard<std::recursive_mutex> locker(g_mutex); g_mutex.lock(); printf("%s %s %d\n", __FILE__, __func__, __LINE__); mul(x); div(y); g_mutex.unlock(); printf("%s %s %d\n", __FILE__, __func__, __LINE__); } private: int i; std::recursive_mutex g_mutex; }; void func() { Complex complex; complex.both(2, 4); } ///g++ mutex.cpp -lpthread int main() { thread t1(func); t1.join(); return 0; }
需要注意的是盡量不要使用遞歸鎖,主要原因如下:
1、需要用到遞歸鎖定的多線程互斥處理往往本身就是可以簡(jiǎn)化的,允許遞歸互斥很容易放縱復(fù)雜邏輯的產(chǎn)生,從而導(dǎo)致一些多線程同步引起的問(wèn)題。
2、遞歸鎖比起非遞歸鎖,效率會(huì)低一些。
帶超時(shí)的互斥量std::timed_mutex
std::timed_mutex是超時(shí)的獨(dú)占鎖,主要用在獲取鎖時(shí)增加超時(shí)等待功能,因?yàn)橛袝r(shí)不知道獲取鎖需要多久,為了不至于一直在等待獲取互斥量,就設(shè)置一個(gè)等待超時(shí)時(shí)間,在超時(shí)后還可以做其他事情。
std::timed_mutex比std::mutex多了兩個(gè)超時(shí)獲取鎖的接口:try_lock_for和try_lock_until,這兩個(gè)接口是用來(lái)設(shè)置獲取互斥量的超時(shí)時(shí)間,使用時(shí)可以用while循環(huán)取不斷地獲取互斥量。std::timed_mutex的基本用法如下所示。
#include <iostream> #include <thread> #include <mutex> #include <chrono> using namespace std; std::timed_mutex g_mutex; void work() { std::chrono::milliseconds timeout(1000); while(true) { if (g_mutex.try_lock_for(timeout)) { cout << std::this_thread::get_id() << ": do work with the mutex" << endl; std::chrono::milliseconds sleepDuration(5000); std::this_thread::sleep_for(sleepDuration); g_mutex.unlock(); std::this_thread::sleep_for(sleepDuration); } else { cout << std::this_thread::get_id() << ": do work without the mutex" << endl; std::chrono::milliseconds sleepDuration(2000); std::this_thread::sleep_for(sleepDuration); } } } ///g++ mutex.cpp -lpthread int main() { std::thread t1(work); std::thread t2(work); t1.join(); t2.join(); return 0; }
在上面的例子中,通過(guò)一個(gè)while循環(huán)不斷地去獲取超時(shí)鎖,如果超時(shí)還沒(méi)有獲取到鎖就會(huì)休眠,再繼續(xù)獲取超時(shí)鎖。
到此這篇關(guān)于C++11互斥量的具體使用的文章就介紹到這了,更多相關(guān)C++11互斥量?jī)?nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Opencv實(shí)現(xiàn)聯(lián)合雙邊濾波
這篇文章主要為大家詳細(xì)介紹了Opencv實(shí)現(xiàn)聯(lián)合雙邊濾波,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10淺談C++高并發(fā)場(chǎng)景下讀多寫少的優(yōu)化方案
本文主要介紹了淺談C++高并發(fā)場(chǎng)景下讀多寫少的優(yōu)化方案,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01C++基于QWidget和QLabel實(shí)現(xiàn)圖片縮放,拉伸與拖拽
這篇文章主要為大家詳細(xì)介紹了C++如何基于QWidget和QLabel實(shí)現(xiàn)圖片縮放、拉伸與拖拽等功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-02-02MFC Frame-Splitter模型實(shí)例原理解析
這篇文章主要介紹了MFC Frame-Splitter模型實(shí)例原理解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07C語(yǔ)言模式實(shí)現(xiàn)C++繼承和多態(tài)的實(shí)例代碼
本篇文章主要介紹了C語(yǔ)言模式實(shí)現(xiàn)C++繼承和多態(tài)的實(shí)例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07C語(yǔ)言中數(shù)據(jù)是如何存儲(chǔ)在內(nèi)存中的
使用編程語(yǔ)言進(jìn)行編程時(shí),需要用到各種變量來(lái)存儲(chǔ)各種信息。變量保留的是它所存儲(chǔ)的值的內(nèi)存位置。這意味著,當(dāng)您創(chuàng)建一個(gè)變量時(shí),就會(huì)在內(nèi)存中保留一些空間。您可能需要存儲(chǔ)各種數(shù)據(jù)類型的信息,操作系統(tǒng)會(huì)根據(jù)變量的數(shù)據(jù)類型,來(lái)分配內(nèi)存和決定在保留內(nèi)存中存儲(chǔ)什么2022-04-04