轻型读写锁(Slim read write lock)

来源:互联网 发布:大数据分析图表 编辑:程序博客网 时间:2024/05/02 01:16

轻型读写锁(Slim read write lock)

分类: Windows核心编程 1506人阅读 评论(1)收藏 举报
winapinullapisocketwindows微软

Windows提供了很多机制进行互斥来保证共享资源不被污染,比如临界区,比如信号量,比如互斥体等等。前一篇对临界区的博文里说过,临界区会先自旋,等自旋不行了再创建内核对象。这样做的原因是从用户模式转到内核模式非常耗费时间,而且常常转换还没完成呢,持有资源的线程已经释放了资源。因此用内核对象的前提都是已知线程对资源的占用都是需要长时间的,比如一堆线程共用一个SOCKET发送数据。

如果对资源的占用时间都不长,那么用临界区或者自旋锁都是不错的选择,但是实际应用当中,你还会面对这样一个情况:

对同一个数据,读取它的次数远远超过更新或者改写它的次数。这好比你有一万个人现在在看你的博客,但是写博客的人只有你一个。这样的情况引发一个问题:

读者之间不应该进行互斥,只有当写者更新数据时才需要对数据进行互斥访问。这时你就需要使用读写锁。

读写锁使得读者之间可以同时读取数据,但是如果写者要更新数据,那么其它读者和写者就不能同时读取或者更新数据

有关微软对读写锁的介绍,你可以看这里:

http://msdn.microsoft.com/en-us/library/aa904937%28VS.85%29.aspx


和临界区一样,使用轻型读写锁的时候,你也要注意几个问题:

1. 这不是内核对象,因此不能继承,不能用Wait函数

2. 初始化啊初始化。

3.读写锁不能递归调用,在多个递归锁中只要有一个独占模式的锁,调用线程就都可能会被挂起(不过上共享锁后再上共享锁,线程还是可以继续进行的,因为读本身就可以同时进行),这个会给出实验进行证明。如果你上了好多个共享锁,那么你还得用同样次数的开锁给它门都开开

4. 开锁函数不需要匹配,你用什么上的锁不需要有什么开,这有实验证明


实验1:开锁是不是得匹配

主线程申请一个独占模式,然后开锁的时候调用共享模式的开锁

第二个线程申请一个共享模式,进入后休眠一秒,然后用独占模式的开锁函数开锁

主线程同时再申请一个共享模式,然后用共享模式的开锁。

看看首先第二个线程能不能执行(因为主线程开的是共享模式的锁,而不是独占模式),然后再看主线程在第二个线程开锁之后还能不能执行。

这是代码:

[cpp] view plaincopyprint?
  1. #include "stdafx.h"   
  2. #include <Windows.h>   
  3. #include <process.h>   
  4. SRWLOCK g_Lock;  
  5. DWORD WINAPI ThreadReader(LPVOID)  
  6. {  
  7.     return 0;  
  8. }  
  9. DWORD WINAPI ThreadWriter(LPVOID)  
  10. {  
  11.     ::AcquireSRWLockShared(&g_Lock);  
  12.     printf("Writer Exec!\n");  
  13.     Sleep(1000);  
  14.     ::ReleaseSRWLockExclusive(&g_Lock);  
  15.     return 0;  
  16. }  
  17. int _tmain(int argc, _TCHAR* argv[])  
  18. {  
  19.     InitializeSRWLock(&g_Lock);  
  20.     ::AcquireSRWLockExclusive(&g_Lock); //申请一个共享模式   
  21.     ::ReleaseSRWLockShared(&g_Lock); //开独占模式  
  22.     HANDLE hTreadWriter = (HANDLE)::_beginthreadex(NULL,NULL,(unsigned int(__stdcall*)(void*))ThreadWriter,NULL,NULL,NULL);  
  23.     WaitForSingleObject(hTreadWriter,INFINITE);  
  24.     ::AcquireSRWLockShared(&g_Lock);  
  25.     ::ReleaseSRWLockShared(&g_Lock); //开独占模式  
  26.     return 0;  
  27. }  
结果是:开锁和上锁连API都不需要配对,你上任何模式的锁,都可以用任何模式的开锁函数打开。不过,为了逻辑清晰,请你自觉调用正确的API。


实验2,递归上锁实验

2.1 递归上1000个共享锁,然后开一个锁,线程2用独占模式上锁。

[cpp] view plaincopyprint?
  1. #include "stdafx.h"   
  2. #include <Windows.h>   
  3. #include <process.h>   
  4. SRWLOCK g_Lock;  
  5. DWORD WINAPI ThreadReader(LPVOID)  
  6. {  
  7.     return 0;  
  8. }  
  9. DWORD WINAPI ThreadWriter(LPVOID)  
  10. {  
  11.     ::AcquireSRWLockExclusive(&g_Lock);  
  12.     printf("Writer Exec!\n");  
  13.     ::ReleaseSRWLockExclusive(&g_Lock);  
  14.     return 0;  
  15. }  
  16. int _tmain(int argc, _TCHAR* argv[])  
  17. {  
  18.     InitializeSRWLock(&g_Lock);  
  19.     for(int i=0;i<1000;i++)  
  20.     ::AcquireSRWLockShared(&g_Lock); //申请一个共享模式   
  21.     //::AcquireSRWLockExclusive(&g_Lock); //再申请一个共享模式  
  22.     ::ReleaseSRWLockShared(&g_Lock); //开一个锁   
  23.     printf("CreateThread!\n");  
  24.     HANDLE hTreadWriter = (HANDLE)::_beginthreadex(NULL,NULL,(unsigned int(__stdcall*)(void*))ThreadWriter,NULL,NULL,NULL);  
  25.     WaitForSingleObject(hTreadWriter,INFINITE);  
  26.     return 0;  
  27. }  

结果是主线程能够创建第二个线程,但是第二个线程不能执行。可见你每一个上锁还是得跟每一个开锁对应起来,上面的代码改成这样就能执行了

[cpp] view plaincopyprint?
  1. for(int i=0;i<1000;i++)  
  2.     {::AcquireSRWLockShared(&g_Lock); //申请一个共享模式   
  3.     //::AcquireSRWLockExclusive(&g_Lock); //再申请一个共享模式  
  4.     ::ReleaseSRWLockShared(&g_Lock);} //开一个锁  

2.2 上一个独占锁,然后再上一个共享锁,看主线程能不能执行。

[cpp] view plaincopyprint?
  1. #include "stdafx.h"   
  2. #include <Windows.h>   
  3. #include <process.h>   
  4. SRWLOCK g_Lock;  
  5. DWORD WINAPI ThreadReader(LPVOID)  
  6. {  
  7.     return 0;  
  8. }  
  9. DWORD WINAPI ThreadWriter(LPVOID)  
  10. {  
  11.     ::AcquireSRWLockExclusive(&g_Lock);  
  12.     printf("Writer Exec!\n");  
  13.     ::ReleaseSRWLockExclusive(&g_Lock);  
  14.     return 0;  
  15. }  
  16. int _tmain(int argc, _TCHAR* argv[])  
  17. {  
  18.     InitializeSRWLock(&g_Lock);  
  19.     ::AcquireSRWLockExclusive(&g_Lock);  
  20.     {::AcquireSRWLockShared(&g_Lock); //申请一个共享模式   
  21.     ::ReleaseSRWLockShared(&g_Lock);} //开一个锁  
  22.     printf("CreateThread!\n");  
  23.     HANDLE hTreadWriter = (HANDLE)::_beginthreadex(NULL,NULL,(unsigned int(__stdcall*)(void*))ThreadWriter,NULL,NULL,NULL);  
  24.     WaitForSingleObject(hTreadWriter,INFINITE);  
  25.     return 0;  
  26. }  
结果是执行不了,根本不能到创建线程。

2.3,反过来

[cpp] view plaincopyprint?
  1. #include "stdafx.h"   
  2. #include <Windows.h>   
  3. #include <process.h>   
  4. SRWLOCK g_Lock;  
  5. DWORD WINAPI ThreadReader(LPVOID)  
  6. {  
  7.     return 0;  
  8. }  
  9. DWORD WINAPI ThreadWriter(LPVOID)  
  10. {  
  11.     ::AcquireSRWLockExclusive(&g_Lock);  
  12.     printf("Writer Exec!\n");  
  13.     ::ReleaseSRWLockExclusive(&g_Lock);  
  14.     return 0;  
  15. }  
  16. int _tmain(int argc, _TCHAR* argv[])  
  17. {  
  18.     InitializeSRWLock(&g_Lock);  
  19.     ::AcquireSRWLockShared(&g_Lock); //申请一个共享模式  
  20.     ::AcquireSRWLockExclusive(&g_Lock);  
  21.     //::ReleaseSRWLockShared(&g_Lock); //开一个锁  
  22.     printf("CreateThread!\n");  
  23.     HANDLE hTreadWriter = (HANDLE)::_beginthreadex(NULL,NULL,(unsigned int(__stdcall*)(void*))ThreadWriter,NULL,NULL,NULL);  
  24.     WaitForSingleObject(hTreadWriter,INFINITE);  
  25.     return 0;  
  26. }  
也是不行。

这个原因也很简单,读锁之间不互斥,但是你一旦申请了独占模式,那么后面再申请任何锁都会因为独占模式还没有解除而使得线程被挂起。


原文链接:http://blog.csdn.net/chris820313/article/details/6818370#


总结: 独占模式或者共享模式的锁,没有解锁,那么accquiresrwlockexclusive会堵死;  独占模式的锁没有解锁, accquiresrwlockshared会堵死。

0 0