要實現讀寫鎖,首先要知道讀寫鎖的特性,除了“讀者可並發,寫者要排它”之外還要考慮避免寫者饑餓的問題。綜合考慮後可以講讀寫鎖的實現總結為一下四點:
1.當已經被施加寫鎖的時候,讀鎖寫鎖都不能在施加(寫鎖只能鎖一次)
2.當已經被施加讀鎖時,還可以繼續施加讀鎖,但不能施加寫鎖
3.有等待的寫者時不能在獲取讀鎖(保證寫者優先)
4.解鎖時有如果寫者在等待,不能喚醒讀者。
我們使用互斥量(mutex)和條件變量來實現讀寫鎖,這也是大多數系統的實現方式。另外本文主要想說明的是讀寫鎖的實現思路,所以這裡只實現最基本的三個操作:申請讀鎖、申請寫鎖、解鎖。而不實現初始化、trylock、銷毀鎖等操作。
typedef struct
2. {
3. pthread_mutex_t rw_mutex; //對整個結構體提供互斥訪問
4. pthread_cond_t rw_condreaders;//用於通知申請讀鎖的線程
5. pthread_cond_t rw_condwriters;//用於通知申請寫鎖的線程
6. int rw_waitreaders; //等待申請讀鎖的線程數
7. int rw_waitwriters; //等待申請寫鎖的線程數
8. int rw_refcount; //表示讀寫鎖的狀態,如果是-1表示它是一個寫鎖
9. }pthread_rwlock_t; //0表示它是可用的,大於0表示當前容納的讀鎖數量
10.
11.
12. int pthread_rwlock_rdlock(pthread_rwlock_t *rw) //申請讀鎖
13. {
14. int result; //返回值(出錯狀態)
15.
16. pthread_mutex_lock(&rw->rw_mutex);
17. //當寫鎖正在使用時不能上讀鎖,當鎖可用但有線程等待申請寫鎖時一樣也不能上讀鎖,這一點體現出來“寫者優先”
18. while(rw->rw_refcount<0||rw->rw_waitwriters>0)
19. {
20. rw->rw_waitreaders++;
21. result=pthread_cond_wait(&rw->rw_condreaders,&rw->rw_mutex);//等待讀條件就緒
22. rw->rw_waitreaders--;
23. if(result!=0)
24. break;
25. }
26. if(result==0)
27. rw->rw_refcount++; //又有一個新的線程獲取了讀鎖
28.
29. pthread_mutex_unlock(&rw->rw_mutex);
30. return (result);
31. }
32.
33.
34. int pthread_rwlock_wrlock(pthread_rwlock_t *rw) //申請寫鎖
35. {
36. int result; //返回值(出錯狀態)
37.
38. pthread_mutex_lock(&rw->rw_mutex);
39. //這裡只檢查鎖是否可用
40. while(rw->rw_refcount!=0)
41. {
42. rw->rw_waitwriters++;
43. result=pthread_cond_wait(&rw->rw_condwriters,&rw->rw_mutex);//等待寫條件就緒
44. rw->rw_waitwriters--;
45. if(result!=0)
46. break;
47. }
48. if(result==0)
49. rw->rw_refcount=-1; //線程獲取了寫鎖
50.
51. pthread_mutex_unlock(&rw->rw_mutex);
52. return (result);
53. }
54.
55.
56.
57.
58. int pthread_rwlock_unlock(pthread_rwlock_t *rw)//釋放鎖(讀鎖、寫鎖)
59. {
60. int result;
61.
62.
63. pthread_mutex_lock(&rw->rw_mutex);
64. if(rw->refcount>0)
65. rw->refcount--;
66. else if(rw->refcount==-1)
67. rw->refcount=0;
68. else
69. printf("rw->refcount=%d\n",rw->refcount);
70. //先看是否有寫者在等待,如果有的話先喚醒寫者,這是“寫者優先”的另一體現
71. if(rw->rw_waitwriters>0)
72. {//不能寫成if(rw->rw_waitwriters>0&&rw->refcount==0)
73. if(rw->refcount==0)
74. result=pthread_cond_signal(&rw->rw_condwriters);
75. }
76. else if(rw->rw_waitreaders>0)
77. result=pthread_cond_signal(&rw->rw_condreaders);
78.
79.
80. pthread_mutex_unlock(rw->rw_mutex);
81. return result;
82. }
不能寫成if(rw->rw_waitwriters>0&&rw->refcount==0)的原因是:
這樣會造成寫者"饑餓",也就是應當保證,當有寫者在等待申請鎖的時候,不能在讓讀者來申請鎖,否則由於讀者可以多次加鎖,一個持續的讀請求流可能永遠阻塞某個等待的寫者。寫成if(rw->rw_waitwriters>0&&rw->refcount==0),當第一個條件滿足而第二個條件不滿足,也就是有寫者在等待,而還有讀者在使用鎖的情況,我們應該僅僅釋放一個讀鎖,別的什麼也不做,而這裡卻會執行else if,導致喚醒等待的讀者,使讀者獲取到鎖。