【C++多線程】讀寫鎖shared_lock/shared_mutex


  轉自princetengC++多線程——讀寫鎖shared_lock/shared_mutex

 何為讀寫鎖

  相比互斥鎖,讀寫鎖允許更高的並行性,互斥量要么鎖住狀態要么不加鎖,而且一次只有一個線程可以加鎖。
讀寫鎖可以有三種狀態:

  • 讀模式加鎖狀態;
  • 寫模式加鎖狀態;
  • 不加鎖狀態;

  只有一個線程可以占有寫模式的讀寫鎖,但是可以有多個線程占有讀模式的讀寫鎖。讀寫鎖也叫做“共享-獨占鎖”,當讀寫鎖以讀模式鎖住時,它是以共享模式鎖住的;當它以寫模式鎖住時,它是以獨占模式鎖住的

  • 當讀寫鎖處於寫加鎖狀態時,在其解鎖之前,所有嘗試對其加鎖的線程都會被阻塞;
  • 當讀寫鎖處於讀加鎖狀態時,所有試圖以讀模式對其加鎖的線程都可以得到訪問權,但是如果想以寫模式對其加鎖,線程將阻塞。這樣也有問題,如果讀者很多,那么寫者將會長時間等待,如果有線程嘗試以寫模式加鎖,那么后續的讀線程將會被阻塞,這樣可以避免鎖長期被讀者占有。

shared_mutex

  C++17起。
  shared_mutex 類是一個同步原語,可用於保護共享數據不被多個線程同時訪問。與便於獨占訪問的其他互斥類型不同,shared_mutex 擁有二個訪問級別:共享 - 多個線程能共享同一互斥的所有權;獨占性 - 僅一個線程能占有互斥。

  • 若一個線程已經通過lock或try_lock獲取獨占鎖(寫鎖),則無其他線程能獲取該鎖(包括共享的)。嘗試獲得讀鎖的線程也會被阻塞。
  • 僅當任何線程均未獲取獨占性鎖時,共享鎖(讀鎖)才能被多個線程獲取(通過 lock_shared 、try_lock_shared )
  • 在一個線程內,同一時刻只能獲取一個鎖(共享或獨占性)。

  成員函數主要包含兩大類:排他性鎖定(寫鎖)和共享鎖定(讀鎖)。

  排他性鎖定

  lock鎖定互斥。若另一線程已鎖定互斥,則lock的調用線程將阻塞執行,直至獲得鎖。若已以任何模式(共享或排他性)占有 mutex 的線程調用 lock ,則行為未定義。也就是說,已經獲得讀模式鎖或者寫模式鎖的線程再次調用lock的話,行為是未定義的。注意:通常不直接使用std::shared_mutex::lock(),而是通過unique_lock或者lock_guard進行管理。

  unlock解鎖互斥。互斥必須為當前執行線程所鎖定,否則行為未定義。如果當前線程不擁有該互斥還去調用unlock,那么就不知道去unlock誰,行為是未定義的。注意:通常不直接調用 unlock() 而是用 std::unique_lock 與 std::lock_guard 管理排他性鎖定。

  共享鎖定

  std::shared_mutex::lock_shared。相比mutex,shared_mutex還擁有lock_shared函數。該函數獲得互斥的共享所有權。若另一線程以排他性所有權保有互斥,則lock_shared的調用者將阻塞執行,直到能取得共享所有權。若已以任何模式(排他性或共享)占有 mutex 的線程調用 lock_shared ,則行為未定義。即:當以讀模式或者寫模式擁有鎖的線程再次調用lock_shared時,行為是未定義的,可能產生死鎖。若多於實現定義最大數量的共享所有者已以共享模式鎖定互斥,則 lock_shared 阻塞執行,直至共享所有者的數量減少。所有者的最大數量保證至少為 10000。注意:通常不直接調用 lock_shared() 而是用 std::shared_lock 管理共享鎖定。shared_lock與unique_lock的使用方法類似。

  std::shared_mutex::unlock_shared。將互斥從調用方線程的共享所有權釋放。當前執行線程必須以共享模式鎖定互斥,否則行為未定義。通常不直接調用 unlock_shared() 而是用 std::shared_lock 管理共享鎖定。

 shared_lock

  C++14起。
  類 shared_lock 是通用共享互斥所有權包裝器(unique_lock則是獨占互斥所有權包裝器),允許延遲鎖定、定時鎖定和鎖所有權的轉移。鎖定 shared_lock,會以共享模式鎖定關聯的共享互斥(std::unique_lock 可用於以排他性模式鎖定)。

  • std::shared_lock<Mutex>::lock以共享模式鎖定關聯互斥。等效於調用 mutex()->lock_shared();
  • std::shared_lock<Mutex>::try_lock嘗試以共享模式鎖定關聯互斥而不阻塞。等效於調用 mutex()->try_lock_shared()。若無關聯互斥,或互斥已被鎖定,則拋出 std::system_error 。
  • std::shared_lock<Mutex>::unlock從共享模式解鎖關聯互斥。等效於調用 mutex()->unlock_shared()。

示例

 1 #include <iostream>
 2 #include <mutex>    //unique_lock
 3 #include <shared_mutex> //shared_mutex shared_lock
 4 #include <thread>
 5 
 6 std::mutex mtx;
 7 
 8 class ThreadSaferCounter
 9 {
10 private:
11     mutable std::shared_mutex mutex_;
12     unsigned int value_ = 0;
13 public:
14     ThreadSaferCounter(/* args */) {};
15     ~ThreadSaferCounter() {};
16     
17     unsigned int get() const {
18         //讀者, 獲取共享鎖, 使用shared_lock
19         std::shared_lock<std::shared_mutex> lck(mutex_);//執行mutex_.lock_shared();
20         return value_;  //lck 析構, 執行mutex_.unlock_shared();
21     }
22 
23     unsigned int increment() {
24         //寫者, 獲取獨占鎖, 使用unique_lock
25         std::unique_lock<std::shared_mutex> lck(mutex_);//執行mutex_.lock();
26         value_++;   //lck 析構, 執行mutex_.unlock();
27         return value_;
28     }
29 
30     void reset() {
31         //寫者, 獲取獨占鎖, 使用unique_lock
32         std::unique_lock<std::shared_mutex> lck(mutex_);//執行mutex_.lock();
33         value_ = 0;   //lck 析構, 執行mutex_.unlock();
34     }
35 };
36 ThreadSaferCounter counter;
37 void reader(int id){
38     while (true)
39     {
40         std::this_thread::sleep_for(std::chrono::seconds(1));
41         std::unique_lock<std::mutex> ulck(mtx);//cout也需要鎖去保護, 否則輸出亂序
42         std::cout << "reader #" << id << " get value " << counter.get() << "\n";
43     }    
44 }
45 
46 void writer(int id){
47     while (true)
48     {
49         std::this_thread::sleep_for(std::chrono::seconds(1));
50         std::unique_lock<std::mutex> ulck(mtx);//cout也需要鎖去保護, 否則輸出亂序
51         std::cout << "writer #" << id << " write value " << counter.increment() << "\n";
52     }
53 }
54 
55 int main()
56 {
57     std::thread rth[10];
58     std::thread wth[10];
59     for(int i=0; i<10; i++){
60         rth[i] = std::thread(reader, i+1);
61     }
62     for(int i=0; i<10; i++){
63         wth[i] = std::thread(writer, i+1);
64     }
65 
66     for(int i=0; i<10; i++){
67         rth[i].join();
68     }
69     for(int i=0; i<10; i++){
70         wth[i].join();
71     }
72     return 0;
73 }

 

 

 




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM