对使用CRITICAL_SECTION实现读写锁的质疑!!!
最近找了很多在WINDOWS平台下实现读写锁的代码,基本上大同小异,最经典的是使用CRITICAL_SECTION来做。
因为CRITICAL_SECTION效率比内核对象要高,所以我也更倾向于尽量使用CRITICAL_SECTION,少使用内核对象。但这样实现我发现有很大的问题,先看下面的代码:
void RWLock::readLock()
{
EnterCriticalSection(&write_lock); //进入写锁临界区
EnterCriticalSection(&v_reader_count); //进入reader_count互斥临界区
reader_count ++;
if( reader_count == 1 )
EnterCriticalSection(&read_lock); //是第一个读者 进入读锁临界区
LeaveCriticalSection(&v_reader_count); //离开reader_count互斥临界区
LeaveCriticalSection(&write_lock); //离开写锁临界区
}
void RWLock::readUnLock()
{
EnterCriticalSection(&v_reader_count); //进入reader_count互斥临界区
reader_count --;
if( reader_count == 0 )
LeaveCriticalSection(&read_lock); //是最后一个读者 离开读锁临界区
LeaveCriticalSection(&v_reader_count); //离开reader_count互斥临界区
}
在这里,我们只关注:read_lock锁:
我们看到,在readLock函数中,如果是第一个读者(即:if( reader_count == 1 ))时,就执行:EnterCriticalSection(&read_lock);以便进入读锁临界区;而在readUnLock函数中,当没有读者时(即:if( reader_count == 0 ) )时,执行:LeaveCriticalSection(&read_lock);
这种实现表面上符合逻辑,没有问题。但却不符合MSDN相关规范。因为:对于EnterCriticalSection和LeaveCriticalSection,MSDN规范要求我们应该是“成对使用”,即:在一个线程中Enter,就应该在同一个线程中Leave。注意这里我们强调的是“同一个线程”。
但是,如果按照上述代码,Enter和Leave很有可能不在同一个线程中执行,
比如:
在线程1中先执行readLock,
然后线路2中执行readLock,
然后线程1结束,
最后线程2结束。
如果是这样的执行顺序,那么就是在线程1中执行EnterCriticalSection,而在线程2中执行LeaveCriticalSection。这显然和规范相悖。这种情况下后果是不可预知的,即:有可能正确,但不保证正确,甚至可能会造成死锁。
我想知道这么做是否保险,或者是否有足够的理论支撑。如果不能这么用,那所有类似的实现读写锁的操作都是错误的!
附:MSDN相关描述:
"If a thread calls LeaveCriticalSection when it does not have ownership of the specified critical section object, an error occurs that may cause another thread using EnterCriticalSection to wait indefinitely."