首页
登录 | 注册

【Windows】线程漫谈——线程同步之Slim读/写锁

本系列意在记录Windwos线程的相关知识点,包括线程基础、线程调度、线程同步、TLS、线程池等。

 

Slim读/写锁

SRWLock的目的和关键段相同,对一个资源进行保护,构造了一段“原子访问”的代码,不让其他线程访问它。但与关键段不同的是SRWLock允许区分想要读取资源值的线程和想要写入资源值的线程,因为仅仅读取资源是不会破坏数据的,下面是Slim读/写锁的简单用法:

SRWLOCK g_srwLock
...
//init SRWLock
InitializeSRWLock(&g_srwLock);
...
//当需要写入资源的时候申请"排他锁"
AcquireSRWLOckExclusive(&g_srwLock);
//执行写入动作
...
//写入结束后释放"排他锁"
ReleaseSRWLockExclusive(&g_srwLock);
//-----------------------------------------
//当需要读取时申请"共享锁"
AcquireSRWLockShared(&g_srwLock);
//执行读操作
...
//读取结束后释放"共享锁"
ReleaseSRWLockShared(&g_srwLock);
 
 
//系统会自动清理g_srwLock,没有别的清理函数


可以看到,存在两种锁用于此机制:"排他锁"和"共享锁"。排他锁需要任何中锁都不存的情况下才能返回,否则阻塞;共享锁在没有排他锁的情况下就可以返回,哪怕有其他共享锁也没关系。这样在写操作之前申请排他锁,在读之前申请共享锁就可以保证共享资源数据不会被意外破坏。另外,只有初始化函数而没有释放函数。

看似SRWLock更关键段十分相似,但相比关键段SRWLock缺乏下面两个特性:

1、不存在对应的TryEnterXXX函数,如果锁已被占用那么只能选择阻塞调用线程;

2、不能递归的获得SRWLock。也就是说一个线程不能为了多次写入资源而多次锁定资源。而关键段可以做到。回想一下,因为关键段在Enter的时候将判断当前线程是否是共享资源的占有者,如果是则会“放行”,并增加引用计数。然而SRWLock始终不关心调用线程是谁。

 

SRWLock配合条件变量

SRWLock的另一个有用之处就是能配合“条件变量”使用,来完成一些更复杂的同步任务。我们假设如下场景:有一个共享的队列,2个服务端线程负责读取队列中的条目以处理,2个客户端线程负责写入队列中的条目以使服务先端线程处理,当队列中没有条目的时候应当挂起服务端线程,直到有条目进入时才被唤醒,另一方面,当队列已满时,客户端线程应当挂起直到服务端至少处理了一个条目,以释放至少一个条目的空间。

首先创建几个共享资源锁,其中SRWLOCK是上文提到的读写锁,CONDITION_VARIABLE就是这里的条件变量:

SRWLOCK  g_srwLock;
CONDITION_VARIABLE g_cvReadyToProduce;//读取线程用于通知写入线程可以开始写入
CONDITION_VARIABLE g_cvReadyToConsume;//写入线程用于通知读取线程可以开始读取


设计如下客户端写入线程流程:

AcquireSRWLockExclusive(&g_srwLock):在写入之前获得排他锁,如果有其他锁,则阻塞;

SleepConditionVariableSRW(&g_cvReadToProduce,&g_srwLock,INFINITE,0):当队列已满时,等待g_cvReadToProduce变量信号(此信号应该由做读取操作的服务端线程发起)。参数:&g_srwLock,INFINITE,0 分别表示暂时释放g_srwLock锁,并永久等待变量信号;

Write Queue...:对队列的写操作,如果在上一步时经过Sleep,并临时释放了g_srwLock锁,在这一步会自动重新获得g_srwLock锁;

ReleaseSRWLockExclusive(&g_srwLock):写完队列后释放排他锁;

WakeAllConditionVariable(&g_cvReadToConsume):向所有正在等待队列中条目的服务端线程发起g_cvReadToConsume信号,通知他们开始读取队列;

 

设计如下的服务端读取流程:

AcquireSRWLockShared(&g_srwLock):在写入之前获得共享锁,如果有排他锁,则会阻塞;

SleepConditionVariableSRW(&g_cvReadToConsume,&g_srwLock,INFINITE,CONDITION_VARIABLE_LOCKMODE_SHARED):当队列空时,等待g_cvReadToConsume变量信号(此信号应该由做写入操作的客户端线程发起)。参数:&g_srwLock,INFINITE,CONDITION_VARIABLE_LOCKMODE_SHARED 分别表示共享g_srwLock锁(不释放),并永久等待变量信号;

Read Queue...:对队列的读操作;

ReleaseSRWLockShared(&g_srwLock):读完队列后释放共享锁;

WakeAllConditionVariable(&g_cvReadToProduce):向所有正在等待队列至少有一个空间条目的客户端线程发起g_cvReadToProduce信号,通知他们可以开始写入队列;

 

上面的过程中我们对一个场景做了设计用到了SRWLock和所谓的条件变量,总结一下条件变量的用法如下:

CONDITION_VARIABLE g_cvReadyToProduce; //创建变量
 
VOID WINAPI InitializeConditionVariable(
  __out  PCONDITION_VARIABLE ConditionVariable
);
 
//等待变量信号
BOOL WINAPI SleepConditionVariableSRW(
  __inout  PCONDITION_VARIABLE ConditionVariable,
  __inout  PSRWLOCK SRWLock,
  __in     DWORD dwMilliseconds,
  __in     ULONG Flags
);
 
//发出变量信号,以唤醒正在等待变量的线程
VOID WINAPI WakeAllConditionVariable(
  __inout  PCONDITION_VARIABLE ConditionVariable
);


前面一篇讲到关键段的时候也提到过条件变量,事实上,关键段也可以使用条件变量,只是API不太相同:

BOOL WINAPI SleepConditionVariableCS(
  __inout  PCONDITION_VARIABLE ConditionVariable,
  __inout  PCRITICAL_SECTION CriticalSection,
  __in     DWORD dwMilliseconds
);
 
VOID WINAPI WakeAllConditionVariable(
  __inout  PCONDITION_VARIABLE ConditionVariable
);


另外,除了WakeAllConditionVariable还有一个WakeConditionVariable,顾名思义,后者是单单唤醒一个正在等待变量的线程,这类似于”事件的自动重置”。

关于更多条件变量的信息可以参见:Using Condition Variables

劳动果实,转载请注明出处:http://www.cnblogs.com/P_Chou/archive/2012/06/24/slim-rw-read-in-thread-sync.html


相关文章

  • 摘要: 本系列意在记录Windwos线程的相关知识点,包括线程基础.线程调度.线程同步.TLS.线程池等. 这篇来说说静态的Interlocked类和ReadWrite锁 .NET中的Interlocked Interlocked的系列方法 ...
  • 摘要: 本系列意在记录Windwos线程的相关知识点,包括线程基础.线程调度.线程同步.TLS.线程池等. 从这篇开始,在线程同步的方法上,开始在.NET平台上做个总结,同时对比Windows原生的API方法.你可以发现其中的联系.   . ...
  • 本系列意在记录Windwos线程的相关知识点,包括线程基础.线程调度.线程同步.TLS.线程池等 本篇介绍与内核对象同步相关的Event对象和Mutex对象   AutoResetEvent和ManualResetEvent 同步事件有两种 ...
  • [IT168 技术文章] JAVA 语言的来源.及特点 在这个高速信息的时代,商家们纷纷把信息.产品做到Internet国际互连网页上.再这些不寻常网页的背后,要属功能齐全.安全可靠的编程语言,Java是当之无愧的.Java是由Sun Mi ...
  • 作者:马光晖一 JAVA 语言的来源.及特点在这个高速信息的时代,商家们纷纷把信息.产品做到Internet国际互连网页上.再这些不寻常网页的背后,要属功能齐全.安全可靠的编程语言,Java是当之无愧的.Java是由Sun Microsys ...
  • synchronized与lock: 1. synchronized是java内置关键字,在jvm层面,Lock是个java类: 2. synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁: 3. synchron ...

2020 unjeep.com webmaster#unjeep.com
12 q. 0.015 s.
京ICP备10005923号