SRWLOCK的目的和关键段相同,对一个资源进行保护,不让其他线程进行访问。与关键段不同的是,SRWLOCK允许我们区分那些想要读取资源的值的线程和想要更新资源的值的线程。
和关键段一样,想用读写锁首先需要分配一个SRWLOCK结构并用InitializeSRWLock函数对它进行初始化,
VOID InitializeSRWLock( PSRWLOCK SRWLock );
一旦SRWLock初始化完成之后,写入者线程可以调用AcquireSRWLockExclusive将SRWLOCK对象的地址作为参数传入,以尝试获得对被保护资源的独占访问权。
VOID AcquireSRWLockExclusive(PSRWLOCK SRWLock);
完成对资源的访问之后,需要调用VOID ReleaseSRWLockExclusive(PSRWLOCK SRWLock)解除对资源的锁定。
对于读取者线程来说,同样有两个步骤,但调用的是下面这两个函数:
VOID AcquireSRWLockShared( PSRWLOCK SRWLock );
VOID ReleaseSRWLockShared(PSRWLOCK SRWLock);
与关键段相比,SRWLock缺乏下面两个特性:
1、不存在TryEnter(Shared/Exclusive)SRWLock之类的函数,如果锁已经被占用,那么调用AcquireSRWLock(Shared/Exclusive)会阻塞调用线程;
2、不能递归获得SRWLock。也就是说,一个线程不能为了多次写入资源而多次锁定资源,然后再多次调用ReleaseSRWLock*来释放对资源的锁定。
条件变量通常和锁一起用。有时候我们想让线程以原子方式吧锁释放并将自己阻塞,直到一个条件成立为止,要实现这样的线程同步是比较复杂的。Windows通过SleepConditionVariableCS或者SleepConditionVariableSRW函数,提供了一种条件变量,来帮助我们简化工作。
BOOL SleepConditionVariableCS(
PCONDITION_VARIABLE pConditionVariable,
PCRITICAL_SECTION pCriticalSection,
DWORD dwMilliseconds
)
VOID SleepConditionVariableSRW(
PCONDITION_VARIABLE pConditionVariable,
PSRWLOCK SRWLock,
DWORD dwMilliseconds,
ULONG Flags
)
当一个线程检测相应的条件满足时,比如存在一个元素可让读取者线程读取,或者有足够的空间让写入者线程插入新的元素,该线程会调用WakeConditionVariable或者WakeConditionVariable,这样阻塞在Sleep*函数中的线程就会被唤醒,
VOID WakeConditionVariable(PCONDITION_VARIABLE pConditionVariable );
VOID WakeAllConditionVariable(PCONDITION_VARIABLE pConditionVariable );
下面的读写锁和条件变量的简单实现:
static int g_arr[5];
static int g_stopFlag = 0;
volatile int g_arrsize = 0;
volatile int g_arrele = 0;
SRWLOCK g_srwlock;
CONDITION_VARIABLE g_cvReadyToWriter; //Signal by writers
CONDITION_VARIABLE g_cvReadyToReader; //Signal by readers
UINT WINAPI WriteToGlobalArr(PVOID pvParam)
{
AcquireSRWLockExclusive(&g_srwlock);
while ((!g_stopFlag) && (g_arrsize == 5))
{
SleepConditionVariableSRW(&g_cvReadyToWriter, &g_srwlock, INFINITE, 0);/* 如果缓存区已满,则写线程等待 */
}
if (g_stopFlag)
{
ReleaseSRWLockExclusive(&g_srwlock);
WakeConditionVariable(&g_cvReadyToWriter);
return 0;
}
g_arr[g_arrsize++] = g_arrele++;
_tprintf(_T("Write %d count=%d tid=%lu\n"), g_arr[g_arrsize - 1], g_arrsize, GetCurrentThreadId());
ReleaseSRWLockExclusive(&g_srwlock);
WakeConditionVariable(&g_cvReadyToReader);
Sleep(1500);
return 0;
}
UINT WINAPI ReadToGlobalArr(PVOID pvParam)
{
AcquireSRWLockShared(&g_srwlock);
while ( (g_arrsize == 0) && ( !g_stopFlag) )
{/* 如果缓存区没数据,读线程等待 */
SleepConditionVariableSRW(&g_cvReadyToReader, &g_srwlock, INFINITE,CONDITION_VARIABLE_LOCKMODE_SHARED);
}
if (g_stopFlag)
{
ReleaseSRWLockShared(&g_srwlock);
WakeConditionVariable(&g_cvReadyToReader);
return 0;
}
_tprintf(_T("Read %d count=%d tid=%lu\n"), g_arr[g_arrsize - 1], g_arrsize, GetCurrentThreadId());
if (g_arrsize == 0 )
{
SleepConditionVariableSRW(&g_cvReadyToReader, &g_srwlock, INFINITE, CONDITION_VARIABLE_LOCKMODE_SHARED);
}
g_arrsize--;
ReleaseSRWLockShared(&g_srwlock);
WakeConditionVariable(&g_cvReadyToWriter);
return 0;
}
//end
main函数如下:
HANDLE hThread[10];
InitializeSRWLock(&g_srwlock);
InitializeConditionVariable(&g_cvReadyToWriter);
InitializeConditionVariable(&g_cvReadyToReader);
//Write thread
for (int i = 0; i<2; i++)
{
hThread[i] = (HANDLE)_beginthreadex(NULL, 0, WriteToGlobalArr, NULL, 0, NULL);
}
//Read thread
for (int i = 2; i<10; i++) {
hThread[i] = (HANDLE)_beginthreadex(NULL, 0, ReadToGlobalArr, NULL, 0, NULL);
}
getchar();
g_stopFlag = 1;
WaitForMultipleObjects(10, hThread, TRUE, INFINITE);
for (int i = 0; i<10; i++) {
CloseHandle(hThread[i]);
}
_tprintf(_T("All threads exit!\n"));