本论坛为只读模式,仅供查阅,不能注册新用户,不能发帖/回帖,有问题可发邮件 xikug.xp (^) gmail.com
查看: 15233|回复: 79

读写锁 [复制链接]

Rank: 2

发表于 2012-3-8 19:33:18 |显示全部楼层
  1. #ifndef _RWLOCK_H_
  2. #define _RWLOCK_H_

  3. #include<windows.h>

  4. class RW_LOCK
  5. {
  6. public:
  7.         /**
  8.        * @Purpose: 构造函数(临界区的初始化)
  9.        */
  10.         RW_LOCK();
  11.         /**
  12.        * @Purpose: 析构函数(删除临界区,析构对象前请保证全部锁解开)
  13.        */
  14.         ~RW_LOCK();
  15.         /**
  16.        * @Purpose: 加读锁
  17.        */
  18.         VOID AcquireShareLock();
  19.         /**
  20.        * @Purpose: 尝试读
  21.        * @Return: true 当前能够读(自动加上读锁,读后要进行解锁操作) , false 当前不能读
  22.        */
  23.         BOOL TryAcquireShareLock();
  24.         /**
  25.        * @Purpose: 解读锁
  26.        */
  27.         VOID ReleaseShareLock();
  28.         /**
  29.        * @Purpose: 加写锁
  30.        */
  31.         VOID AcquireExclusiveLock();
  32.         /**
  33.        * @Purpose: 尝试写
  34.        * @Return: true 当前能够写(自动加上写锁,写后要进行解锁操作) , false 当前不能写
  35.        */
  36.         BOOL TryAcquireExclusiveLock();
  37.         /**
  38.        * @Purpose: 解写锁
  39.        */
  40.         VOID ReleaseExclusiveLock();

  41.         BOOL IsInitOk();

  42. private:
  43.         BOOL bIsInitOk;//是否初始化成功

  44.         INT iNowReaderCount; //读者计数

  45.         CRITICAL_SECTION ExclusiveLock; //写锁

  46.         HANDLE hShareLock; //读锁

  47.         CRITICAL_SECTION ShareReaderCountLock; //读者计数访问互斥锁
  48. };

  49. RW_LOCK::RW_LOCK()
  50. {
  51.         iNowReaderCount = 0;

  52.         hShareLock=CreateEvent(NULL,FALSE,TRUE,NULL);

  53.         if (hShareLock)
  54.         {
  55.                 bIsInitOk=TRUE;

  56.                 InitializeCriticalSection(&ExclusiveLock);

  57.                 InitializeCriticalSection(&ShareReaderCountLock);
  58.         }else
  59.         {
  60.                 bIsInitOk=FALSE;
  61.         }
  62. }

  63. RW_LOCK::~RW_LOCK()
  64. {
  65.         if (bIsInitOk)
  66.         {
  67.                 DeleteCriticalSection(&ExclusiveLock);

  68.                 DeleteCriticalSection(&ShareReaderCountLock);

  69.                 CloseHandle(hShareLock);

  70.                 hShareLock=NULL;

  71.                 bIsInitOk=FALSE;
  72.         }
  73. }

  74. BOOL RW_LOCK::IsInitOk()
  75. {
  76.         return bIsInitOk;
  77. }

  78. BOOL RW_LOCK::TryAcquireShareLock()
  79. {
  80.         if(TryEnterCriticalSection(&ExclusiveLock))
  81.         {
  82.                 EnterCriticalSection(&ShareReaderCountLock);

  83.                 iNowReaderCount ++;

  84.                 if( iNowReaderCount == 1 )
  85.                 {
  86.                         WaitForSingleObject(hShareLock,INFINITE);
  87.                 }
  88.                        
  89.                 LeaveCriticalSection(&ShareReaderCountLock);

  90.                 LeaveCriticalSection(&ExclusiveLock);

  91.                 return true;
  92.         }else
  93.         {
  94.                 return false;
  95.         }
  96. }


  97. VOID RW_LOCK::AcquireShareLock()
  98. {
  99.         EnterCriticalSection(&ExclusiveLock);

  100.         EnterCriticalSection(&ShareReaderCountLock);

  101.         iNowReaderCount ++;

  102.         if( iNowReaderCount == 1 )
  103.         {
  104.                 WaitForSingleObject(hShareLock,INFINITE);
  105.         }
  106.                
  107.         LeaveCriticalSection(&ShareReaderCountLock);

  108.         LeaveCriticalSection(&ExclusiveLock);
  109. }

  110. VOID RW_LOCK::ReleaseShareLock()
  111. {
  112.         EnterCriticalSection(&ShareReaderCountLock);

  113.         iNowReaderCount --;

  114.         if( iNowReaderCount == 0 )
  115.         {
  116.                 SetEvent(hShareLock);
  117.         }

  118.         LeaveCriticalSection(&ShareReaderCountLock);
  119. }

  120. VOID RW_LOCK::AcquireExclusiveLock()
  121. {
  122.         EnterCriticalSection(&ExclusiveLock);

  123.         WaitForSingleObject(hShareLock,INFINITE);
  124. }

  125. BOOL RW_LOCK::TryAcquireExclusiveLock()
  126. {
  127.         if(TryEnterCriticalSection(&ExclusiveLock))
  128.         {
  129.                 WaitForSingleObject(hShareLock,INFINITE);

  130.                 return true;
  131.         }else
  132.         {
  133.                 return false;
  134.         }
  135. }

  136. VOID RW_LOCK::ReleaseExclusiveLock()
  137. {
  138.         SetEvent(hShareLock);

  139.         LeaveCriticalSection(&ExclusiveLock);
  140. }

  141. #endif
复制代码
目前网络上见到的使用临界体的读写锁版本,几乎都有可能出现,在一个线程内EnterCriticalSection,然后却在另一个线程内LeaveCriticalSection的情况。
我这个版本不会有这个问题。

锁本身是写优先,允许同时多个读,只允许同时一个写;读写不能同时存在。

其它不说了,上代码:

Rank: 2

发表于 2012-3-8 20:01:52 |显示全部楼层
本帖最后由 KiCall 于 2012-3-8 20:36 编辑

RWLock.7z (3.64 KB, 下载次数: 965)

附件是封装过的版本

Rank: 1

发表于 2012-3-8 20:42:35 |显示全部楼层
几乎都有可能出现,在一个线程内EnterCriticalSection,然后却在另一个线程内LeaveCriticalSection的情况,
,这种情况怎样才能出现?

Rank: 2

发表于 2012-3-8 21:49:05 |显示全部楼层
C语言版本
  1. // RWLock.cpp : 定义 DLL 应用程序的导出函数。
  2. //
  3. #include "stdafx.h"
  4. #include <stdlib.h>
  5. #include "RWLock.h"

  6. PVOID __stdcall CreateRWLock()
  7. {
  8.         PRW_LOCK pLock=NULL;

  9.         do
  10.         {
  11.                 pLock=(PRW_LOCK)malloc(sizeof(RW_LOCK));

  12.                 if (!pLock)
  13.                 {
  14.                         break;
  15.                 }

  16.                 RtlZeroMemory(pLock,sizeof(RW_LOCK));

  17.                 pLock->iNowReaderCount=0;

  18.                 pLock->hShareLock=CreateEvent(NULL,FALSE,TRUE,NULL);

  19.                 if (pLock)
  20.                 {
  21.                         InitializeCriticalSection(&pLock->ExclusiveLock);

  22.                         InitializeCriticalSection(&pLock->ShareReaderCountLock);
  23.                 }else
  24.                 {
  25.                         free(pLock);

  26.                         pLock=NULL;
  27.                 }
  28.         } while (FALSE);

  29.         return (PVOID)pLock;
  30. }

  31. VOID __stdcall DeleteRWLock(PVOID pLock)
  32. {
  33.         PRW_LOCK pRwLock=(PRW_LOCK)pLock;

  34.         if (pRwLock->hShareLock)
  35.         {
  36.                 DeleteCriticalSection(&pRwLock->ExclusiveLock);

  37.                 DeleteCriticalSection(&pRwLock->ShareReaderCountLock);

  38.                 CloseHandle(pRwLock->hShareLock);

  39.                 free(pLock);
  40.         }
  41. }

  42. VOID __stdcall AcquireShareLock(PVOID pLock)
  43. {
  44.         PRW_LOCK pRwLock=(PRW_LOCK)pLock;

  45.         EnterCriticalSection(&pRwLock->ExclusiveLock);

  46.         EnterCriticalSection(&pRwLock->ShareReaderCountLock);

  47.         pRwLock->iNowReaderCount ++;

  48.         if( pRwLock->iNowReaderCount == 1 )
  49.         {
  50.                 WaitForSingleObject(pRwLock->hShareLock,INFINITE);
  51.         }

  52.         LeaveCriticalSection(&pRwLock->ShareReaderCountLock);

  53.         LeaveCriticalSection(&pRwLock->ExclusiveLock);
  54. }

  55. BOOL __stdcall TryAcquireShareLock(PVOID pLock)
  56. {
  57.         PRW_LOCK pRwLock=(PRW_LOCK)pLock;

  58.         BOOL bIsAcquired=FALSE;

  59.         do
  60.         {
  61.                 bIsAcquired=TryEnterCriticalSection(&pRwLock->ExclusiveLock);

  62.                 if (!bIsAcquired)
  63.                 {
  64.                         break;
  65.                 }

  66.                 EnterCriticalSection(&pRwLock->ShareReaderCountLock);

  67.                 pRwLock->iNowReaderCount ++;

  68.                 if( pRwLock->iNowReaderCount == 1 )
  69.                 {
  70.                         WaitForSingleObject(pRwLock->hShareLock,INFINITE);
  71.                 }

  72.                 LeaveCriticalSection(&pRwLock->ShareReaderCountLock);

  73.                 LeaveCriticalSection(&pRwLock->ExclusiveLock);

  74.         } while (FALSE);

  75.         return bIsAcquired;
  76. }

  77. VOID __stdcall ReleaseShareLock(PVOID pLock)
  78. {
  79.         PRW_LOCK pRwLock=(PRW_LOCK)pLock;

  80.         EnterCriticalSection(&pRwLock->ShareReaderCountLock);

  81.         pRwLock->iNowReaderCount --;

  82.         if( pRwLock->iNowReaderCount == 0 )
  83.         {
  84.                 SetEvent(pRwLock->hShareLock);
  85.         }

  86.         LeaveCriticalSection(&pRwLock->ShareReaderCountLock);
  87. }

  88. VOID __stdcall AcquireExclusiveLock(PVOID pLock)
  89. {
  90.         PRW_LOCK pRwLock=(PRW_LOCK)pLock;

  91.         EnterCriticalSection(&pRwLock->ExclusiveLock);

  92.         WaitForSingleObject(pRwLock->hShareLock,INFINITE);
  93. }

  94. BOOL __stdcall TryAcquireExclusiveLock(PVOID pLock)
  95. {
  96.         PRW_LOCK pRwLock=(PRW_LOCK)pLock;

  97.         BOOL bIsAcquired=FALSE;

  98.         do
  99.         {
  100.                 bIsAcquired=TryEnterCriticalSection(&pRwLock->ExclusiveLock);

  101.                 if (!bIsAcquired)
  102.                 {
  103.                         break;
  104.                 }

  105.                 WaitForSingleObject(pRwLock->hShareLock,INFINITE);
  106.         } while (FALSE);

  107.         return bIsAcquired;
  108. }

  109. VOID __stdcall ReleaseExclusiveLock(PVOID pLock)
  110. {
  111.         PRW_LOCK pRwLock=(PRW_LOCK)pLock;

  112.         SetEvent(pRwLock->hShareLock);

  113.         LeaveCriticalSection(&pRwLock->ExclusiveLock);
  114. }
复制代码

Rank: 2

发表于 2012-3-8 21:50:39 |显示全部楼层
  1. #ifndef _RWLOCK_H_
  2. #define _RWLOCK_H_

  3. typedef struct _RW_LOCK
  4. {
  5.         INT iNowReaderCount; //读者计数

  6.         CRITICAL_SECTION ExclusiveLock; //写锁

  7.         HANDLE hShareLock; //读锁

  8.         CRITICAL_SECTION ShareReaderCountLock; //读者计数访问互斥锁
  9. }RW_LOCK,*PRW_LOCK;


  10. #ifdef __cplusplus
  11. extern "C"
  12. {
  13. #endif

  14. PVOID __stdcall CreateRWLock();

  15. VOID __stdcall DeleteRWLock(PVOID pRWLock);

  16. VOID __stdcall AcquireShareLock(PVOID pRWLock);

  17. BOOL __stdcall TryAcquireShareLock(PVOID pRWLock);

  18. VOID __stdcall ReleaseShareLock(PVOID pRWLock);

  19. VOID __stdcall AcquireExclusiveLock(PVOID pRWLock);

  20. BOOL __stdcall TryAcquireExclusiveLock(PVOID pRWLock);

  21. VOID __stdcall ReleaseExclusiveLock(PVOID pRWLock);

  22. #ifdef __cplusplus
  23. }
  24. #endif

  25. #endif
复制代码

Rank: 2

发表于 2012-3-8 21:58:53 |显示全部楼层
haras 发表于 2012-3-8 20:42
几乎都有可能出现,在一个线程内EnterCriticalSection,然后却在另一个线程内LeaveCriticalSection的情况,
, ...

网上的那些版本
无一例外的,三个锁变量都是三个CriticalSection
代码中有段判断
if(iNowReaderCount==1)
{
    LeaveCriticalSection(ReaderCountLock);
}

就是这段代码会出问题。

例如,线程1首先第一个获取到读权限,在线程1释放读权限之前(此时iNowReaderCount==1),线程2这时也获取到读权限(此时iNowReaderCount=2)。接着线程1马上释放读权限,此时iNowReadercount==1,所以线程1不会LeaveCriticalSection。但事实上,EnterCriticalSection和LeaveCriticalSection必须在同一个线程内成对出现。

现在我把这个ReaderCountLock换成了同步事件的句柄,因为WaitEvent与SetEvent并不要求在同一个线程,所以,不会有问题了。

Rank: 2

发表于 2012-3-8 22:00:00 |显示全部楼层
错了
刚刚的例子代码该是
if(iNowReaderCount==0)
{
     LeaveCriticalSection(ReaderCountLock);
}

Rank: 2

发表于 2012-3-8 22:04:28 |显示全部楼层
C预言版本中,有段代码有点问题。。。手误
  1. if (pLock->hShareLock)
  2.                 {
  3.                         InitializeCriticalSection(&pLock->ExclusiveLock);

  4.                         InitializeCriticalSection(&pLock->ShareReaderCountLock);
  5.                 }else
  6.                 {
  7.                         free(pLock);

  8.                         pLock=NULL;
  9.                 }
复制代码
该是这样

Rank: 1

发表于 2012-3-9 11:21:34 |显示全部楼层
KiCall 发表于 2012-3-8 21:58
网上的那些版本
无一例外的,三个锁变量都是三个CriticalSection
代码中有段判断

无一例外说的太绝对了.

http://software.intel.com/zh-cn/blogs/2012/02/14/win32-c/
在 Win32 下用 C++ 实现多线程读写锁

Rank: 1

发表于 2012-3-9 13:27:37 |显示全部楼层
求学习

Rank: 1

发表于 2012-3-9 18:33:36 |显示全部楼层
先看看

Rank: 1

发表于 2012-3-9 18:44:13 |显示全部楼层
不支持重入.实际应用的时候会很悲剧..
建议参考WINDOWS内核中ERESOURCE的实现,另外可以参考的代码是OSR的一个OSRESOURCE的读写锁实现。另外就是,很多情况下,读写锁没太大必要使用。你需要很高的并发么?你确定用读写锁就能达到高并发的目的么?

Rank: 1

发表于 2012-3-9 18:45:02 |显示全部楼层
  1. void TestRW_LOCK()
  2. {
  3.     RW_LOCK lock;
  4.     lock.AcquireExclusiveLock();
  5.     lock.AcquireShareLock();
  6. }
复制代码
上例就是测试代码.自己可以测试下看看.

Rank: 2

发表于 2012-3-9 20:35:04 |显示全部楼层
eaglenet 发表于 2012-3-9 18:44
不支持重入.实际应用的时候会很悲剧..
建议参考WINDOWS内核中ERESOURCE的实现,另外可以参考的代码是OSR的 ...

支持重入。。。仅仅是写操作会是独占而已。。。请睁大眼睛看清楚!
读操作时可以重入的
理论上,写操作支持重入的话,本来就会出问题
比如,对一个链表操作,几个线程都同时在上面删除或者添加节点
这个明显会出问题

Rank: 2

发表于 2012-3-9 20:38:00 |显示全部楼层
eaglenet 发表于 2012-3-9 18:45
上例就是测试代码.自己可以测试下看看.

这叫重入吗?这叫锁类型的转换!!
由独占锁转换为共享锁。
锁类型转换为一定目的就是:
抢在下一个独占锁之前,获取到共享锁。
这个没意义,也违背了独占优先的设计初衷!

Rank: 2

发表于 2012-3-9 20:40:20 |显示全部楼层
eaglenet 发表于 2012-3-9 18:45
上例就是测试代码.自己可以测试下看看.

同样,反过来,由共享锁转换为独占锁,这个是基本理论上都不可能的事情,而且没有任何意义。
即使是windows的读写锁,也仅仅提供了由独占转换为共享,不支持由共享转换为独占。


实际上,不管哪种转换,都没有存在的意义。

Rank: 2

发表于 2012-3-9 20:45:05 |显示全部楼层
eaglenet 发表于 2012-3-9 18:44
不支持重入.实际应用的时候会很悲剧..
建议参考WINDOWS内核中ERESOURCE的实现,另外可以参考的代码是OSR的 ...

关于读写锁
你说没必要使用,这个估计你没有遇到过每秒10万次以上的并发的情况。
就为了偶尔几次的写操作,就把每秒10万次以上的并发读操作给序列化化了(必须加锁,因为虽然写操作很少,但是会有,所以不加锁的话,会出问题,如果不用读写锁,普通锁的话,明显会把所有操作序列化),你试试你的系统会不会死。。。。。

Rank: 2

发表于 2012-3-9 20:52:10 |显示全部楼层
cntrump 发表于 2012-3-9 11:21
无一例外说的太绝对了.

http://software.intel.com/zh-cn/blogs/2012/02/14/win32-c/

先看清楚。。。。说的是使用临界体的情况。
很多人认为别人使用事件,互斥体等,理论上,性能低于临界体。但是,他们忘了,临界体的获取和释放必须在同一个线程。但是事件,互斥体这些,就不需要在同一个线程。
我说的正是这个问题。
你给的那个链接。。。。本来别人就不是使用临界体的。

Rank: 1

发表于 2012-3-9 22:20:41 |显示全部楼层
KiCall 发表于 2012-3-9 20:35
支持重入。。。仅仅是写操作会是独占而已。。。请睁大眼睛看清楚!
读操作时可以重入的
理论上,写操作支 ...

您真厉害,说出了同步的基本需求..

Rank: 1

发表于 2012-3-9 22:21:57 |显示全部楼层
KiCall 发表于 2012-3-9 20:38
这叫重入吗?这叫锁类型的转换!!
由独占锁转换为共享锁。
锁类型转换为一定目的就是:

您真理想化啊..代码编写的时候有极大可能在拿到写锁的情况下去操作一些原先要求拿读锁的情况.
您需要登录后才可以回帖 登录 | 立即加入

Archiver|手机版|第8个男人 - 论坛为只读模式,仅供查阅

GMT+8, 2019-3-20 19:06 , Processed in 0.052222 second(s), 11 queries .

Design by pvo.cn

© 2011 Pvo Inc.

回顶部