Windows平台下的读写锁
Windows平台下的读写锁
简单介绍Windows平台下的读写锁以及实现.
背景介绍
Windows在Vista 和 Server2008以后才开始提供读写锁API,即SRW系列函数(InitializeSRWLock, AcquireSRWLockShared, AcquireSRWLockExclusive等).考虑到目前Windows XP的装机量,只能自己实现一个读写锁了.
读写锁的目的和要求
读写锁的最基本目的是读锁可以共享,写锁必须独占.另外,我认为还有两点需要特别考虑:
1. 如果有写锁请求在等待,则新的读锁请求也应该等待.否则程序可能永远也没有机会获得写锁.
2. 一旦写锁被释放,所有在等待中的读锁和写锁请求应该可以公平竞争,而不管请求的先后,只要之前已经在等待就应该有获得锁的机会.
如果说第一点还可以再斟酌一下的话,第二点应该是必须要保证的.
读写锁的实现
总体思路比较简单:读写锁内部维护一些状态值,用临界端 CRITICAL_SECTION保护这些状态值访问的原子性和事件对象配合实现等待.
进入锁的情况:
1. 如果当前锁状态为空闲,则不管写锁还是读锁请求都允许进入,并设置相应的状态值.
2. 如果当前锁状态是读,新的写锁请求需要等待事件通知,并把写锁等待计数加一.
3. 如果当前锁状态是读,新的读锁请求:
3.1 如果没有写锁请求在等待,则允许读锁进入,并把读锁计数加一.
3.2 如果有写锁请求正在等待,则等待事件通知,并读锁等待计数加一(这样做的目的如上文所述,要使写锁有机会进入).
4. 如果当前锁状态为写,则不管读锁请求还是写锁请求,都等待事件通知并分别给读锁等待计数或者写锁等待计数加一.
解锁的情况:
如果锁释放时,读锁等待计数或者写锁等待计数不为0,则触发事件对象.
我使用手动事件对象,这样的话一旦锁被释放,所有正在等待的锁请求都将被激活,然后重新以竞争临界段的方式竞争锁进入权以保证公平性.不管等待请求时的先后,只要是锁释放前进入等待状态则锁一旦释放获得进入权的机会是均等的.
后记
我在实现读写锁之前,用Google搜索过,找到的几种Windows读写锁实现都不甚理想,主要问题如下:
1. 等待事件对象时占有了互斥量,这是一个非常不好的设计,这样的话其他锁请求将被阻塞在临界段外部,只有一个锁请求阻塞在Wait函数,对其他锁请求是不公平的.
2. 没考虑我开始提的目的2,即写锁可能永远都没机会进入.我不知道这个算不算缺点,但是自少在我的应用中,这种情况是不允许出现的,所以我重新设计过一个读写锁.
实现过程中一个让我很纠结的问题是:到底用自动事件还是手动事件. 用手动事件的好处是一旦触发,所有等待中的请求都被激活然后重新竞争,逻辑简单明了.缺点是可能会有很多冗余操作,比如有若干写锁还若干读锁请求正在等待进入,一旦锁释放,虽然全部请求(线程)都被激活,但是肯定只有一个请求能够进入,竞争失败的请求测试一下条件后继续挂起.如果使用自动事件,只有一个锁请求线程会被唤醒(Wait函数的特点,被唤醒的那个线程等同于已经竞争成功),似乎效率更高一些.获得进入权的锁请求再根据等待的情况决定是否继续触发事件对象:如果还有读请求和写请求在等待,则不触发;如果只有读请求在等待,则再触发一次以使其他读请求可以进入.考虑再三,我还是决定采用手动事件,毕竟在读锁的数量远大于写锁数量的情况下(这也是读写锁比较常见的场景)速度更快一些(不需要等待多次事件).
附录1 - C++代码
- #define RWLOCK_IDLE 0 /* 空闲 */
- #define RWLOCK_R 0x01 /* 读锁 */
- #define RWLOCK_W 0x02 /* 写锁 */
- class RWLock
- {
- private:
- int _st; /* 锁状态值 */
- int _rlockCount; /* 读锁计数 */
- int _rwaitingCount; /* 读等待计数 */
- int _wwaitingCount; /* 写等待计数 */
- HANDLE _ev; /* 通知事件 Event */
- //HANDLE _stLock; /* 访问状态值互斥量 */ /* 如果需要等待超时,则用 Mutex */
- CRITICAL_SECTION _stLock;
- public:
- RWLock(void);
- ~RWLock(void);
- void rlock();
- void wlock();
- void unlock();
- };
- RWLock::RWLock(void)
- : _rlockCount(0),
- _st(RWLOCK_IDLE),
- _rwaitingCount(0),
- _wwaitingCount(0)
- {
- //_stLock = CreateMutex(NULL, FALSE, NULL);
- //assert(_stLock != INVALID_HANDLE_VALUE);
- InitializeCriticalSection(&_stLock);
- /*
- * 假设当前有多个读锁请求正在等待写锁释放,那么当写锁被释放时,所有这些读锁都应该有机会获得执行.
- */
- _ev = CreateEvent(NULL, TRUE, FALSE, NULL);
- assert(_ev != INVALID_HANDLE_VALUE);
- }
- RWLock::~RWLock(void)
- {
- //CloseHandle(_stLock);
- DeleteCriticalSection(&_stLock);
- CloseHandle(_ev);
- }
- void RWLock::rlock()
- {
- bool isWaitReturn = false;
- while(1)
- {
- //WaitForSingleObject(_stLock, INFINITE);
- EnterCriticalSection(&_stLock);
- if(isWaitReturn)
- {
- /*
- * 等待事件返回,重新竞争锁.
- */
- --_rwaitingCount;
- }
- if(_st == RWLOCK_IDLE)
- {
- /*
- * 空闲状态,直接得到控制权
- */
- _st = RWLOCK_R;
- _rlockCount++;
- //ReleaseMutex(_stLock);
- LeaveCriticalSection(&_stLock);
- break;
- }
- else if( _st == RWLOCK_R)
- {
- if(_wwaitingCount > 0)
- {
- /*
- * 有写锁正在等待,则一起等待,以使写锁能获得竞争机会.
- */
- ++_rwaitingCount;
- ResetEvent(_ev);
- //SignalObjectAndWait(_stLock, _ev, INFINITE, FALSE);
- LeaveCriticalSection(&_stLock);
- /*
- * 虽然 LeaveCriticalSection() 和 WaitForSingleObject() 之间有一个时间窗口,
- * 但是由于windows平台的事件信号是不会丢失的,所以没有问题.
- */
- WaitForSingleObject(_ev, INFINITE);
- /*
- * 等待返回,继续尝试加锁.
- */
- isWaitReturn = true;
- }
- else
- {
- /*
- * 得到读锁,计数+1
- */
- ++_rlockCount;
- //ReleaseMutex(_stLock);
- LeaveCriticalSection(&_stLock);
- break;
- }
- }
- else if(_st == RWLOCK_W)
- {
- /*
- * 等待写锁释放
- */
- ++_rwaitingCount;
- ResetEvent(_ev);
- //SignalObjectAndWait(_stLock, _ev, INFINITE, FALSE);
- LeaveCriticalSection(&_stLock);
- WaitForSingleObject(_ev, INFINITE);
- /*
- * 等待返回,继续尝试加锁.
- */
- isWaitReturn = true;
- }
- else
- {
- assert(0);
- break;
- }
- }
- }
- void RWLock::wlock()
- {
- bool isWaitReturn = false;
- while(1)
- {
- //WaitForSingleObject(_stLock, INFINITE);
- EnterCriticalSection(&_stLock);
- if(isWaitReturn) --_wwaitingCount;
- if(_st == RWLOCK_IDLE)
- {
- _st = RWLOCK_W;
- //ReleaseMutex(_stLock);
- LeaveCriticalSection(&_stLock);
- break;
- }
- else
- {
- ++_wwaitingCount;
- ResetEvent(_ev);
- //SignalObjectAndWait(_stLock, _ev, INFINITE, FALSE);
- LeaveCriticalSection(&_stLock);
- WaitForSingleObject(_ev, INFINITE);
- isWaitReturn = true;
- }
- }
- }
- void RWLock::unlock()
- {
- //WaitForSingleObject(_stLock, INFINITE);
- EnterCriticalSection(&_stLock);
- if(_rlockCount > 0)
- {
- /* 读锁解锁 */
- --_rlockCount;
- if( 0 == _rlockCount)
- {
- _st = RWLOCK_IDLE;
- /* 释放 */
- if( _wwaitingCount > 0 || _rwaitingCount > 0 )
- {
- /*
- * 此时有锁请求正在等待,激活所有等待的线程.(手动事件).
- * 使这些请求重新竞争锁.
- */
- SetEvent(_ev);
- }
- else
- {
- /* 空闲 */
- }
- }
- else
- {
- /* 还有读锁 */
- }
- }
- else
- {
- _st = RWLOCK_IDLE;
- /* 写锁解锁 */
- if( _wwaitingCount > 0 || _rwaitingCount > 0 )
- {
- /*
- * 如果在占有互斥量_stLock的情况下,触发事件,那么可能会使一些锁请求不能得到竞争机会.
- * 假设调用unlock时,另一个线程正好调用rlock或者wlock.如果不释放互斥量,只有之前已经等待的锁请求有机会获得锁控制权.
- */
- SetEvent(_ev);
- }
- else
- {
- /* 空闲 */
- }
- }
- //ReleaseMutex(_stLock);
- LeaveCriticalSection(&_stLock);
- }
Windows平台下的读写锁的更多相关文章
- 不同WINDOWS平台下磁盘逻辑扇区的直接读写
不同WINDOWS平台下磁盘逻辑扇区的直接读写 关键字:VWIN32.中断.DeviceIoControl 一.概述 在DOS操作系统下,通过BIOS的INT13.DOS的INT25(绝对读).INT ...
- Windows平台下利用APM来做负载均衡方案 - 负载均衡(下)
概述 我们在上一篇Windows平台分布式架构实践 - 负载均衡中讨论了Windows平台下通过NLB(Network Load Balancer) 来实现网站的负载均衡,并且通过压力测试演示了它的效 ...
- windows平台下基于VisualStudio的Clang安装和配置
LLVM 是一个开源的编译器架构,它已经被成功应用到多个应用领域.Clang是 LLVM 的一个编译器前端,它目前支持 C, C++, Objective-C 以及 Objective-C++ 等编程 ...
- [转]Windows平台下安装Hadoop
1.安装JDK1.6或更高版本 官网下载JDK,安装时注意,最好不要安装到带有空格的路径名下,例如:Programe Files,否则在配置Hadoop的配置文件时会找不到JDK(按相关说法,配置文件 ...
- MongoDB学习总结(一) —— Windows平台下安装
> 基本概念 MongoDB是一个基于分布式文件存储的开源数据库系统,皆在为WEB应用提供可扩展的高性能数据存储解决方案.MongoDB将数据存储为一个文档,数据结构由键值key=>val ...
- windows平台下基于QT和OpenCV搭建图像处理平台
在之前的博客中,已经分别比较详细地阐述了"windows平台下基于VS和OpenCV"以及"Linux平台下基于QT和OpenCV"搭建图像处理框架,并 ...
- Windows平台下的内存泄漏检测
在C/C++中内存泄漏是一个不可避免的问题,很多新手甚至有许多老手也会犯这样的错误,下面说明一下在windows平台下如何检测内存泄漏. 在windows平台下内存泄漏检测的原理大致如下. 1. 在分 ...
- Spotlight on Mysql在Windows平台下的安装及使用简介
Spotlight on Mysql在Windows平台下的安装及使用简介 by:授客 QQ:1033553122 1. 测试环境 Win7 64位 mysql-connector-odbc- ...
- 排错-windows平台下访问oracle em出现空白的解决方法
排错-windows平台下访问oracle em出现空白的解决方法 by:授客 QQ:1033553122 问题描述 IE浏览器本地访问oem,出现空白页面,就左上角有一行字符 http://loca ...
随机推荐
- C++常量(C++数值常量、字符串常量、符号常量)
http://see.xidian.edu.cn/cpp/biancheng/view/104.html 字符串常量 用双撇号括起来的部分就是字符串常量,如"abc"," ...
- SciTE 文本编辑器
个人一直使用,强大,轻型基于 Scintilla. http://www.scintilla.org/SciTEDownload.html 解压缩后,能右键文件打开 修改注册表, 使能资源管理器 右键 ...
- Android SQlite详解
在项目开发中,我们或多或少都会用到数据库.在Android中,我们一般使用SQLite,因为Android在android.database.sqlite包封装了很多SQLite操作的API.我自己写 ...
- ios 开发 收起键盘的小技巧
在UIViewController中收起键盘,除了调用相应控件的resignFirstResponder方法外,还有另外三种方法: 1.重载UIViewController中的touchesBegin ...
- flush tables 好危险啊
请看图 +----+------+-----------+------+------------+------+-------------------------+------------------ ...
- 字符串(string)转json
第一种方式: 使用js函数eval(); testJson=eval(testJson);是错误的转换方式. 正确的转换方式需要加(): testJson = eval("(" + ...
- python 练习 6
#!/usr/bin/python # -*- coding: utf-8 -*- from collections import deque from math import log10 def p ...
- VBA中find的一些使用方法
用excel处理数据的时候,无论是使用VBA还是函数,查找和引用都是两大主要的工作,VBA中的find系列的方法(find.findnext.Range.FindPrevious)返回range对象, ...
- 父类中“this” 指向问题
“this.字段”如果出现在父类代码中,指的就是父类属性. “this.方法”不管出现在父类还是子类代码中,指的都是子类方法. “this.字段”如果出现在子类代码中,指的就是子类属性. 在程序的时候 ...
- hdu----(4521)小明系列问题——小明序列
小明系列问题——小明序列 Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others)Tota ...