在多執行緒的使用中,如果我們建立了多個執行緒,多個執行緒之間的執行順序是由cpu來完成排程的,因此我們如果需要在多執行緒中進行資料共享和通訊,就需要注意資料安全的問題,有可能我在乙個執行緒中正在執行對資料的操作,此時cpu通過上下文切換,把當前執行緒切換掉了,開始執行了別的執行緒,而別的執行緒本來希望的是拿到我處理過後的資料,而現在拿到的資料是還沒有處理完成的資料,這樣當然就出現了資料傳輸的錯誤,這樣的錯誤一般出現在讀寫操作,如果當前程序是唯讀的話,是不會影響資料安全的。為了避免這種我在操作中被切換中斷的行為發生,就需要引入互斥量的概念,也就是我們常說的鎖。
1,mutex
mutex是乙個class,其中包含有成員函式,lock()、unlock()、try_lock(),
std::mutex mymutex;
mymutex.
lock()
;//加鎖
//....想要被保護的操作
mynutex.
unlock()
;//解鎖
需要注意的是,加鎖解鎖需要成對使用,加鎖後一定就要完成鎖的解鎖,而且我前面說了lock是嘗試獲取這把鎖並加鎖,因此如果多個執行緒中只有乙個執行緒可以獲取到當前的鎖,如果乙個獲取到了,另乙個執行緒只有在鎖被釋放後才能繼續嘗試獲取鎖,並且lock和unlock在效率上是極低的。
2、lock_guard
由於我們使用普通mutex必須時刻記住加鎖後要解鎖,而在工程中,我們很可能就忽視掉了這種小細節而造成了程式無法正常執行丟擲異常,因此,引入了lock_guard。lock_guard採用了」資源分配時初始化」(raii)方法來加鎖、解鎖,即我們只要建立乙個lock_guard物件時,就對lock_guard執行了加鎖,而在物件被析構時,自動完成解鎖,這樣就不會造成加鎖後忘記解鎖而造成其他執行緒的阻塞。
std::lock_guard
myguard
(mymutex)
;
我們在執行以上語句構造物件的時候,就會自動完成鎖的加鎖操作,並且在結束生命週期時自動完成析構,並且完成解鎖,這也就是raii的方法。
我們也可以給lock_guard傳入第二引數adopt_lock,來表示當前鎖我們是已經鎖過的,構造lock_guard時不需要自動加鎖,析構時正常解鎖。
std::lock_guard
myguard
(mynutex,adopt_lock)
;
3、unique_lock
unique_lock是一種更為靈活的互斥量,從名字上來看可能類似於unique_ptr,就是說以獨佔所有權的方式(unique owership)管理mutex物件的上鎖和解鎖操作,即在unique_lock物件的宣告週期內,它所管理的鎖物件會一直保持上鎖狀態;而unique_lock的生命週期結束之後,它所管理的鎖物件會被解鎖。和unique_ptr相同,不支援拷貝構造,但是可以移動構造。unique_lock具有lock_guard的所有功能,而且更為靈活,但占用記憶體更大。為什麼說他更為靈活呢。它具有幾個成員函式,如lock,unlock,try_to_lock,try_lock_for,try_lock_until,release,這些函式的存在,讓我們可以更方便和自由的使用這個鎖。
std::unique_lock
myunique
(mymutex)
;
可以使用上述語句建立乙個已經加鎖的unique_lock,這和lock_guard很相似,但是不同點是lock_guard只有在物件被析構時鎖才被被unlock,但是unique_lock可以呼叫unlock成員函式,在任意時刻完成手動解鎖,這樣就靈活了很多,而且要知道我們用鎖包裹住的**量越大,程式執行的效率越低,所以我們盡量要使得包裹的**少,unlock就可以滿足我們的這些要求,給程式帶來了更高的效率。
unique_lock也可以有第二引數,defer_lock和try_to_lock。
std::unique_lock
myunique
(mymutex,try_to_lock)
;std::unique_lock
myunique
(mymutex,defer_lock)
;
使用try_to_lock第二引數,執行到這條語句時,會嘗試去和其他執行緒爭搶鎖的所有權,如果得到了所有權,返回就會加鎖,然後執行我們之後的**,如果沒有鎖上,也不會在此行語句造成阻塞。
使用defer_lock第二引數,表示我們在構造時,不需要程式自動加鎖,我們可以在之後的使用中在完成加鎖操作。
4、還有別的幾種鎖
如 timed_mutex,recursive_mutex,recursive_timed_mutex,分別是帶超時處理的互斥鎖,遞迴的互斥鎖,帶超時處理的遞迴互斥鎖。
超時處理互斥鎖我們可以使用try_lock_for和try_lock_until成員函式來規定我們是等待一段時間還是等待到某一時刻,我們可以這樣使用
std::timed_mutex mytimemutex;
// 超時的獨佔互斥量
std::chrono::milliseconds dura
(100);
bool flag=mytimemutex.
try_lock_for
(dura)
;// 等待一段時間 在這段時間嘗試獲取鎖頭
bool flag=mytimemutex.
try_lock_for
(std::chrono::steady_clock::
now(
)+dura)
;// 等到到某某時刻 比如當前時間100ms後
其返回值都是bool型變數,因此我們可將其用在判斷型語句中,如果滿足條件獲取到了鎖完成加鎖,返回true ,執行特定語句,否則執行其他語句。應用較為靈活。
遞迴的互斥鎖也是一種特殊的鎖,前面說過的mutex都只能加鎖一次,我們多次加鎖多次解鎖都會丟擲異常,而recursive_mutex我們可以在乙個執行緒中,針對同一鎖對其進行多次的加鎖和解鎖而不會造成死鎖現象。
c 多執行緒 互斥量
我們在做程式時,有時候希望在一台電腦上面只有乙個程序例項在執行,利用mutex互斥量可以實現了這個功能,方法及步驟如下 接下來分控制台程式和winform程式兩種情況下實現此功能 1.控制台程式 csharp view plain copy using system using system.col...
Linux 多執行緒互斥量互斥
同乙個程序中的多個執行緒共享所在程序的記憶體資源,當多個執行緒在同一時刻同時訪問同一種共享資源時,需要相互協調,以避免出現資料的不一致和覆蓋等問題,執行緒之間的協調和通訊的就叫做執行緒的同步問題,執行緒同步的思路 讓多個執行緒依次訪問共享資源,而不是並行 mutex被建立時可以有初始值,表示mute...
多執行緒 共享互斥量
就像之前我們提過的一樣,很多執行緒需要互斥量進行相互鎖定,因為很多關鍵資料和操作並不是執行緒安全,多執行緒處理很有可能會出很大的問題。但是互斥量又會帶來乙個問題,我就舉個例子來說明 比如說,現在有一組資料,可以對它進行讀寫操作。但是一般情況讀寫要分開操作,因為這樣才可以讓後續處理更好的執行。很明顯的...