1. 程式人生 > >讀寫鎖---理解及測試例項

讀寫鎖---理解及測試例項

讀寫鎖

互斥鎖與讀寫鎖的區別

同樣都是鎖,互斥鎖與讀寫鎖之間什麼區別和聯絡?

互斥鎖:當使用互斥鎖時,每次只能有一個執行緒拿到鎖,進入臨界區,訪問臨界資源。

在對臨界資源的訪問中,可以將訪問方式份為兩種:

(1)讀取資料

(2)修改資料

我們對與臨界資源加鎖,是為了保護臨界資源的安全性與正確性,防止多個執行緒在同一時間段中對臨界資源同時修改。但是,讀取資料時我們只對於資料進行讀取,並不對與資料進行修改,所以不會對資料的安全性和準確性造成問題。

但是,對於互斥鎖來說,它不管執行緒是讀資料還是寫資料,每次都只能有一個執行緒拿到鎖,等訪問完後再還回鎖。

如果在遇到有許多大量執行緒僅讀取資料,少量執行緒進行資料修改的情景,使用互斥鎖,所有的讀取資料的執行緒都必須排隊挨個讀取,與修改資料的執行緒並無區別。為了提高效率,出現了讀寫鎖

來適用該種場景,提高效率。

讀寫鎖

讀寫鎖分為兩種鎖,一種為讀鎖,用於讀取資料,一種為寫鎖,用來修改資料。當某一執行緒專門來讀取共享資料時就使用讀鎖,當某一新執行緒要修改共享資料時,便使用寫鎖。

讀鎖(共享鎖):
  • 讀鎖可以被多個執行緒同時拿到,當臨界區被讀鎖鎖住時,其他使用讀鎖的執行緒仍舊可以拿到鎖,進入臨界區讀取臨界資源。
  • 當臨界區被寫鎖鎖住時,讀鎖去獲得鎖時,會被阻塞直到寫鎖解鎖
寫鎖(獨佔鎖):
  • 寫鎖每次只能被一個執行緒拿到,當一個讀者已經拿到寫鎖,進入臨界區時,其他後面到達的不管是讀鎖還是寫鎖都會被阻塞到解鎖
  • 當一個寫鎖去訪問臨界資源,如果在臨界區中仍有一個或多個讀者在讀取資料時或者有一個寫者在進行寫資料,寫者將被阻塞到所有的讀者訪問完,將鎖解鎖後,再獲得鎖。

讀寫鎖的優點:讀鎖的使用提高了高併發度,可以使多個讀者在任意時刻高併發讀取資料。也保證了寫入者在寫入資料時,不會被其他寫入者與讀取者干擾,保證了資料的正確性與安全性。

適合場景:當有大量執行緒頻繁的讀取資料,只有少量的的執行緒對資料進行修改時

讀寫鎖函式
#include <pthread.h>
使用pthread_rwlock_t 型別宣告一個讀寫鎖變數

讀寫鎖的初始化

int pthread_rwlock_init(pthread_rwlock_t *rwptr,pthread_rwlockattr_t *attr)
//初始化一個讀寫鎖,第一個引數為鎖變數指標,第二個引數為屬性,當為NULL使用預設屬性

int pthread_rwlock_destroy(pthread_rwlock_t *rwptr)  //銷燬讀寫鎖

讀寫鎖的獲取與釋放

int pthread_rwlock_rdlock(pthread_rwlock_t *rwptr)  //讀鎖
int pthread_rwlock_wrlock(pthread_rwlock_t *rwptr)  //寫鎖

int pthread_rwlock_unlock(pthread_rwlock_t *rwptr)  //將讀鎖或寫鎖解鎖

嘗試獲取鎖

當臨界區被一個寫鎖鎖住時,之後想要獲得鎖的讀者和寫者獲得鎖失敗時都將被阻塞,所以,如果使用以下兩個函式,如果獲得鎖失敗時,會返回一個錯誤,並不會被阻塞。

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwptr) 
//嘗試獲得讀鎖

int pthread_rwlock_trywrlock(pthread_rwlock_t *rwptr)
//嘗試獲得寫鎖

測試例項:

#include <iostream>
 #include <pthread.h>
 #include <unistd.h>
 const int pth_num = 5;
 int number = 0;
 pthread_rwlock_t rwlock;
 void* write_routine(void* arg)
 {//寫者執行緒函式

   while(1)
   {
     usleep(1000);
     pthread_rwlock_wrlock(&rwlock);
     std::cout<<"write change the number: "<<++number<<std::endl;
     pthread_rwlock_unlock(&rwlock);
   }

 }

 void* read_routine(void* arg)
 {//讀者執行緒函式
   int i = *(int*)arg;
   while(1)
   {
     pthread_rwlock_rdlock(&rwlock);
     std::cout<<"reader "<<i<<"get the number: "<<number<<std::endl;
     pthread_rwlock_unlock(&rwlock);
     usleep(300);
   }

   delete (int*)arg;
 }

 int main()
 {

   pthread_t id[pth_num];

   pthread_rwlock_init(&rwlock,NULL);


   pthread_create(&id[0], NULL, write_routine, NULL);
   for(int i = 1; i < pth_num; i++)
   {
       int* p = new int;
       p = &i;
       pthread_create(&id[i], NULL, read_routine, (void*)p);
       usleep(550);
   }

   for(int i = 0; i < pth_num; i++)
   {
     pthread_join(id[i], NULL);
   }

   pthread_rwlock_destroy(&rwlock);
   return 0;
 }