首页
社区
课程
招聘
[原创]原创读写锁,求测试
发表于: 2014-1-20 12:11 14034

[原创]原创读写锁,求测试

2014-1-20 12:11
14034
本来是N年前自己写着玩的一个东西,近来打算在产品中使用,所以借助大家的力量帮忙测试下,欢迎拍砖、喷饭。
有重大改进或者bug提出者,奖励100元话费。

 typedef struct _LAST_ACQUIRE_TYPE
{
	BOOLEAN bIsAcquireRead;

	struct _LAST_ACQUIRE_TYPE *pPre;

}LAST_ACQUIRE_TYPE,*PLAST_ACQUIRE_TYPE;

typedef struct _RW_LOCK
{
	INT iNowReaderCount; //读者计数

	PLAST_ACQUIRE_TYPE pLastAcquireType;

	ULONG ulTlsForWriteIndex;

	ULONG ulTlsForReadIndex;

	CRITICAL_SECTION ExclusiveLock; //写锁

	HANDLE hShareLock; //读锁

	CRITICAL_SECTION ShareReaderCountLock; //读者计数访问互斥锁

	LONG volatile lNowIsRelease;

}RW_LOCK,*PRW_LOCK;


#ifndef _RWLOCK_H_
#define _RWLOCK_H_

#ifdef __cplusplus

extern "C"
{
#endif

PVOID __stdcall CreateRWLock();

VOID __stdcall DeleteRWLock(PVOID pRWLock);

VOID __stdcall AcquireShareLock(PVOID pRWLock);

BOOL __stdcall TryAcquireShareLock(PVOID pRWLock);

VOID __stdcall AcquireExclusiveLock(PVOID pRWLock);

BOOL __stdcall TryAcquireExclusiveLock(PVOID pRWLock);

VOID __stdcall ReleaseLock(PVOID pLock);

#ifdef __cplusplus
}
#endif

#ifdef __cplusplus

typedef class _RW_LOCK_TYPE
{
public:
	_RW_LOCK_TYPE()
	{
		m_RwLockHandle=CreateRWLock();
	}

	~_RW_LOCK_TYPE()
	{
		if(IsLockOk())
		{
			DeleteRWLock(m_RwLockHandle);

			m_RwLockHandle=NULL;
		}
	}

	BOOLEAN IsLockOk(){return (m_RwLockHandle!=NULL);}

	PVOID m_RwLockHandle;

}RW_LOCK_TYPE,*PRW_LOCK_TYPE;

typedef class _WLock
{
public:
	_WLock(RW_LOCK_TYPE &RwLock)
	{
		if (RwLock.IsLockOk())
		{
			AcquireExclusiveLock(RwLock.m_RwLockHandle);

			m_RwLockHandle=RwLock.m_RwLockHandle;
		}
	}

	~_WLock()
	{
		if (IsLockOk())
		{
			ReleaseLock(m_RwLockHandle);
		}
	}

	BOOLEAN IsLockOk(){return (m_RwLockHandle!=NULL);}

private:
	PVOID m_RwLockHandle;

}WLock,*PWLock;

typedef class _RLock
{
public:
	_RLock(RW_LOCK_TYPE &RwLock)
	{
		if (RwLock.IsLockOk())
		{
			AcquireShareLock(RwLock.m_RwLockHandle);

			m_RwLockHandle=RwLock.m_RwLockHandle;
		}
	}

	~_RLock()
	{
		if (IsLockOk())
		{
			ReleaseLock(m_RwLockHandle);
		}
	}

	BOOLEAN IsLockOk(){return (m_RwLockHandle!=NULL);}

private:
	PVOID m_RwLockHandle;

}RLock,*PRLock;

//C++用法
/*
RW_LOCK_TYPE RwLock;//定义锁变量

RLock ReadLock(RwLock);//定义读锁变量而且同时加读锁(析构时会解锁),对于读锁,不要求new和delete在同一线程

WLock WriteLock(RwLock);//定义写锁变量而且同时加写锁(析构时会解锁),写锁支持重入,对于写锁,要求new和delete必须在同一线程

读锁和写锁同一线程混合调用时,允许先获取写锁,接着获取读锁,反过来的就不行,会死锁,也就是,

允许这样的顺序:

WLock WriteLock(RwLock);

RLock ReadLock(RwLock);

WLock WriteLock0(RwLock);

禁止这样的顺序:

RLock ReadLock(RwLock);

WLock WriteLock(RwLock);

WLock WriteLock0(RwLock);


也就是,在一个线程内,只要最开始的调用的是写锁,那么这个线程内,后续的读写锁顺序就可以随意,不受限制

*/
#endif

#endif





PVOID __stdcall CreateRWLock()
{
	PRW_LOCK pLock=NULL;

	BOOLEAN bIsOk=FALSE;

	do 
	{
		pLock=(PRW_LOCK)malloc(sizeof(RW_LOCK));

		if (!pLock)
		{
			break;
		}

		RtlZeroMemory(pLock,sizeof(RW_LOCK));

		pLock->ulTlsForWriteIndex=TlsAlloc();

		if (pLock->ulTlsForWriteIndex==(ULONG)-1)
		{
			break;
		}

		pLock->ulTlsForReadIndex=TlsAlloc();

		if (pLock->ulTlsForReadIndex==(ULONG)-1)
		{
			break;
		}

		pLock->iNowReaderCount=0;

		pLock->hShareLock=CreateEvent(NULL,FALSE,TRUE,NULL);

		if (!pLock->hShareLock)
		{
			break;
		}

		InitializeCriticalSection(&pLock->ExclusiveLock);

		InitializeCriticalSection(&pLock->ShareReaderCountLock);

		pLock->pLastAcquireType=NULL;

		bIsOk=TRUE;

	} while (FALSE);

	if (!bIsOk &&
		pLock)
	{
		if (pLock->hShareLock)
		{
			CloseHandle(pLock->hShareLock);

			pLock->hShareLock=NULL;
		}

		if (pLock->ulTlsForWriteIndex!=(ULONG)-1)
		{
			TlsFree(pLock->ulTlsForWriteIndex);

			pLock->ulTlsForWriteIndex=(ULONG)-1;
		}

		if (pLock->ulTlsForReadIndex!=(ULONG)-1)
		{
			TlsFree(pLock->ulTlsForReadIndex);

			pLock->ulTlsForReadIndex=(ULONG)-1;
		}

		free(pLock);

		pLock=NULL;
	}

	return (PVOID)pLock;
}

VOID __stdcall DeleteRWLock(PVOID pLock)
{
	PRW_LOCK pRwLock=(PRW_LOCK)pLock;

	PLAST_ACQUIRE_TYPE pTmpLastType=NULL;

	if (pRwLock)
	{

		if (pRwLock->ulTlsForWriteIndex!=(ULONG)-1)
		{
			TlsFree(pRwLock->ulTlsForWriteIndex);

			pRwLock->ulTlsForWriteIndex=(ULONG)-1;
		}

		if (pRwLock->ulTlsForReadIndex!=(ULONG)-1)
		{
			TlsFree(pRwLock->ulTlsForReadIndex);

			pRwLock->ulTlsForReadIndex=(ULONG)-1;
		}

		DeleteCriticalSection(&pRwLock->ExclusiveLock);

		DeleteCriticalSection(&pRwLock->ShareReaderCountLock);

		if (pRwLock->hShareLock)
		{
			CloseHandle(pRwLock->hShareLock);

			pRwLock->hShareLock=NULL;
		}

		pTmpLastType=pRwLock->pLastAcquireType;

		if (pTmpLastType)
		{
			pRwLock->pLastAcquireType=pTmpLastType->pPre;
		}else
		{
			pRwLock->pLastAcquireType=NULL;
		}

		while(pTmpLastType)
		{
			free(pTmpLastType);

			pTmpLastType=pRwLock->pLastAcquireType;

			if (pTmpLastType)
			{
				pRwLock->pLastAcquireType=pTmpLastType->pPre;
			}else
			{
				pRwLock->pLastAcquireType=NULL;
			}
		}

		free(pLock);

		pLock=NULL;

	}
}

VOID __stdcall AcquireShareLock(PVOID pLock)
{
	PRW_LOCK pRwLock=(PRW_LOCK)pLock;

	ULONG_PTR ulExclusiveFlag=0;

	ULONG_PTR ulSharedFlag=0;

	PLAST_ACQUIRE_TYPE pLastType=malloc(sizeof(LAST_ACQUIRE_TYPE));

	DWORD dwWaitValue=0;

	do 
	{
		if (!pLastType)
		{
			__debugbreak();
		}

		ulSharedFlag=(ULONG_PTR)TlsGetValue(pRwLock->ulTlsForReadIndex);

		ulExclusiveFlag=(ULONG_PTR)TlsGetValue(pRwLock->ulTlsForWriteIndex);


		//判断当前线程的读写锁状态

		if (ulSharedFlag!=1 &&
			ulSharedFlag!=0)//当前线程在读锁状态,增加计数就够了
		{
			EnterCriticalSection(&pRwLock->ShareReaderCountLock);

			pRwLock->iNowReaderCount++;

			pLastType->bIsAcquireRead=TRUE;

			pLastType->pPre=pRwLock->pLastAcquireType;

			pRwLock->pLastAcquireType=pLastType;

			LeaveCriticalSection(&pRwLock->ShareReaderCountLock);

			if (!TlsSetValue(pRwLock->ulTlsForReadIndex,(PVOID)(ulSharedFlag+1)))
			{
				__debugbreak();
			}

			break;
		}

		if (ulExclusiveFlag!=1 &&
			ulExclusiveFlag!=0)//当前线程占有写锁,所以只要记录下当前操作就够了
		{
			pLastType->bIsAcquireRead=TRUE;

			pLastType->pPre=pRwLock->pLastAcquireType;

			pRwLock->pLastAcquireType=pLastType;

			break;
		}


		//判断其它线程的读写锁状态

		EnterCriticalSection(&pRwLock->ExclusiveLock);//当前如果有其它线程企图获取写锁,先把这个读锁请求阻塞,使得写优先;同时,这个逻辑还有个作用就是判断其它线程是否在写状态,如果是,这里会阻塞

		EnterCriticalSection(&pRwLock->ShareReaderCountLock);

		if (pRwLock->iNowReaderCount>0)//有其它线程在读锁状态
		{
			pRwLock->iNowReaderCount++;

			pLastType->bIsAcquireRead=TRUE;

			pLastType->pPre=pRwLock->pLastAcquireType;

			pRwLock->pLastAcquireType=pLastType;

			LeaveCriticalSection(&pRwLock->ShareReaderCountLock);

			LeaveCriticalSection(&pRwLock->ExclusiveLock);

			ulSharedFlag=1;

			if (!TlsSetValue(pRwLock->ulTlsForReadIndex,(PVOID)(ulSharedFlag+1)))
			{
				__debugbreak();
			}

			break;
		}

		//没有任何线程占有任何读写锁

		dwWaitValue=WaitForSingleObjectEx(pRwLock->hShareLock,INFINITE,FALSE);

		if(dwWaitValue!=WAIT_OBJECT_0)//获取读锁,使得企图获取写锁的线程,阻塞在这里
		{
			__debugbreak();
		}

		pLastType->bIsAcquireRead=TRUE;

		pLastType->pPre=pRwLock->pLastAcquireType;

		pRwLock->pLastAcquireType=pLastType;

		pRwLock->iNowReaderCount++;

		LeaveCriticalSection(&pRwLock->ShareReaderCountLock);

		LeaveCriticalSection(&pRwLock->ExclusiveLock);

		ulSharedFlag=1;

		if (!TlsSetValue(pRwLock->ulTlsForReadIndex,(PVOID)(ulSharedFlag+1)))
		{
			__debugbreak();
		}

	} while (FALSE);
}

BOOL __stdcall TryAcquireShareLock(PVOID pLock)
{
	PRW_LOCK pRwLock=(PRW_LOCK)pLock;

	BOOL bIsAcquired=FALSE;

	ULONG_PTR ulExclusiveFlag=0;

	PLAST_ACQUIRE_TYPE pLastType=NULL;

	ULONG_PTR ulSharedFlag=0;

	DWORD dwWaitValue=0;

	do 
	{
		pLastType=malloc(sizeof(LAST_ACQUIRE_TYPE));

		if (!pLastType)
		{
			break;
		}

		//首先判断当前线程的读写锁状态

		ulSharedFlag=(ULONG_PTR)TlsGetValue(pRwLock->ulTlsForReadIndex);

		ulExclusiveFlag=(ULONG_PTR)TlsGetValue(pRwLock->ulTlsForWriteIndex);

		if (ulSharedFlag!=1 &&
			ulSharedFlag!=0)//当前线程在读锁状态。只要计数增加,不需要新获取读锁状态
		{
			if (!TlsSetValue(pRwLock->ulTlsForReadIndex,(PVOID)(ulSharedFlag+1)))
			{
				bIsAcquired=FALSE;

				break;
			}

			bIsAcquired=TryEnterCriticalSection(&pRwLock->ShareReaderCountLock);

			if (!bIsAcquired)
			{
				break;
			}

			pLastType->bIsAcquireRead=TRUE;

			pLastType->pPre=pRwLock->pLastAcquireType;

			pRwLock->pLastAcquireType=pLastType;

			pLastType=NULL;

			pRwLock->iNowReaderCount ++;

			LeaveCriticalSection(&pRwLock->ShareReaderCountLock);

			break;
		}


		//当前线程不占有读锁但是占有写锁,只要记录下当前操作就够了
		if (ulExclusiveFlag!=1 &&
			ulExclusiveFlag!=0)
		{
			pLastType->bIsAcquireRead=TRUE;

			pLastType->pPre=pRwLock->pLastAcquireType;

			pRwLock->pLastAcquireType=pLastType;

			break;
		}


		//到这里说明当前线程不占有任和锁,继续判断其它线程的读写锁状态

		bIsAcquired=TryEnterCriticalSection(&pRwLock->ExclusiveLock);

		if (!bIsAcquired)//说明其它线程占有写锁,失败返回,否则会阻塞
		{
			break;
		}

		bIsAcquired=TryEnterCriticalSection(&pRwLock->ShareReaderCountLock);

		if (!bIsAcquired)
		{
			LeaveCriticalSection(&pRwLock->ExclusiveLock);

			break;
		}

		if (pRwLock->iNowReaderCount>0)
		{
			ulSharedFlag=1;

			if (!TlsSetValue(pRwLock->ulTlsForReadIndex,(PVOID)(ulSharedFlag+1)))
			{
				bIsAcquired=FALSE;

				LeaveCriticalSection(&pRwLock->ShareReaderCountLock);

				LeaveCriticalSection(&pRwLock->ExclusiveLock);

				break;
			}

			pRwLock->iNowReaderCount++;

			pLastType->bIsAcquireRead=TRUE;

			pLastType->pPre=pRwLock->pLastAcquireType;

			pRwLock->pLastAcquireType=pLastType;

			pLastType=NULL;

			LeaveCriticalSection(&pRwLock->ShareReaderCountLock);

			LeaveCriticalSection(&pRwLock->ExclusiveLock);

			break;
		}


		//到这里说明没有线程占有任何锁

		dwWaitValue=WaitForSingleObjectEx(pRwLock->hShareLock,INFINITE,FALSE);//获取读锁,使得企图获取写锁的线程,阻塞在这里

		if(dwWaitValue!=WAIT_OBJECT_0)
		{
			bIsAcquired=FALSE;

			LeaveCriticalSection(&pRwLock->ShareReaderCountLock);

			LeaveCriticalSection(&pRwLock->ExclusiveLock);
		}

		ulSharedFlag=1;

		if (!TlsSetValue(pRwLock->ulTlsForReadIndex,(PVOID)(ulSharedFlag+1)))
		{
			bIsAcquired=FALSE;

			SetEvent(pRwLock->hShareLock);

			LeaveCriticalSection(&pRwLock->ShareReaderCountLock);

			LeaveCriticalSection(&pRwLock->ExclusiveLock);

			break;
		}

		pLastType->bIsAcquireRead=TRUE;

		pLastType->pPre=pRwLock->pLastAcquireType;

		pRwLock->pLastAcquireType=pLastType;

		pLastType=NULL;

		pRwLock->iNowReaderCount++;

		LeaveCriticalSection(&pRwLock->ShareReaderCountLock);

		LeaveCriticalSection(&pRwLock->ExclusiveLock);

	} while (FALSE);

	if (pLastType)
	{
		free(pLastType);

		pLastType=NULL;
	}

	return bIsAcquired;
}

VOID __stdcall ReleaseShareLock(PVOID pLock)
{
	PRW_LOCK pRwLock=(PRW_LOCK)pLock;

	ULONG_PTR ulExclusiveFlag=0;

	ULONG_PTR ulSharedFlag=0;

	do 
	{
		ulSharedFlag=(ULONG_PTR)TlsGetValue(pRwLock->ulTlsForReadIndex);

		if (ulSharedFlag!=0 &&
			ulSharedFlag!=1)//当前线程在读锁状态
		{
			EnterCriticalSection(&pRwLock->ShareReaderCountLock);

			if (pRwLock->iNowReaderCount==0)
			{
				__debugbreak();
			}

			pRwLock->iNowReaderCount--;

			if (!TlsSetValue(pRwLock->ulTlsForReadIndex,(PVOID)(ulSharedFlag-1)))
			{
				__debugbreak();
			}

			if( pRwLock->iNowReaderCount == 0 )
			{
				SetEvent(pRwLock->hShareLock);
			}

			LeaveCriticalSection(&pRwLock->ShareReaderCountLock);

			break;
		}

	} while (FALSE);
}

VOID __stdcall AcquireExclusiveLock(PVOID pLock)
{
	PRW_LOCK pRwLock=(PRW_LOCK)pLock;

	ULONG_PTR ulExclusiveFlag=0;

	ULONG_PTR ulShareFlag=0;

	PLAST_ACQUIRE_TYPE pLastType=malloc(sizeof(LAST_ACQUIRE_TYPE));

	DWORD dwWaitValue=0;

	do 
	{
		if (!pLastType)
		{
			__debugbreak();
		}

		ulShareFlag=(ULONG_PTR)TlsGetValue(pRwLock->ulTlsForReadIndex);

		ulExclusiveFlag=(ULONG_PTR)TlsGetValue(pRwLock->ulTlsForWriteIndex);

		//先判断当前线程的读写锁状态

		////当前线程处于读锁状态,若获取写锁,会死锁在WaitForSingleObjectEx里,所以直接让程序崩溃,便于找出bug位置
		if (ulShareFlag!=1 &&
			ulShareFlag!=0)
		{
			__debugbreak();
		}

		//当前线程占有写锁,写锁计数+1
		if(ulExclusiveFlag!=1 &&
			ulExclusiveFlag!=0)
		{
			EnterCriticalSection(&pRwLock->ExclusiveLock);

			if (!TlsSetValue(pRwLock->ulTlsForWriteIndex,(PVOID)(ulExclusiveFlag+1)))
			{
				__debugbreak();
			}

			pLastType->bIsAcquireRead=FALSE;

			pLastType->pPre=pRwLock->pLastAcquireType;

			pRwLock->pLastAcquireType=pLastType;

			break;

		}

		//当前线程不占有读写锁

		ulExclusiveFlag=1;//初始化当前线程占有写锁的标记为首次占用

		//以下一行代码主要这样几个目的:

		//1、若其它线程占有写锁,会阻塞在这个锁里
		//2、若其它线程不占有写锁,那这行代码使得后续企图获取写锁的线程阻塞在这个锁里
		//3、使得那些不占有读锁的线程,企图获取读锁时,阻塞在这个锁里,达到写优先的目的
		//4、占有写锁
		EnterCriticalSection(&pRwLock->ExclusiveLock);


		//本线程获取到写锁的基本条件,但还需要等待其它占有读锁的线程释放读锁
		//任何时候,只会有一个线程到达这里

		dwWaitValue=WaitForSingleObjectEx(pRwLock->hShareLock,INFINITE,FALSE);//获取读锁,使得企图获取写锁的线程,阻塞在这里

		if(dwWaitValue!=WAIT_OBJECT_0)
		{
			__debugbreak();
		}

		SetEvent(pRwLock->hShareLock);

		if (!TlsSetValue(pRwLock->ulTlsForWriteIndex,(PVOID)(ulExclusiveFlag+1)))
		{
			__debugbreak();
		}

		pLastType->bIsAcquireRead=FALSE;

		pLastType->pPre=pRwLock->pLastAcquireType;

		pRwLock->pLastAcquireType=pLastType;

	} while (FALSE);

	return;
}

BOOL __stdcall TryAcquireExclusiveLock(PVOID pLock)
{
	PRW_LOCK pRwLock=(PRW_LOCK)pLock;

	BOOL bIsAcquired=FALSE;

	PLAST_ACQUIRE_TYPE pLastType=NULL;

	ULONG_PTR ulExclusiveFlag=0;

	ULONG_PTR ulSharedFlag=0;

	do 
	{
		pLastType=(PLAST_ACQUIRE_TYPE)malloc(sizeof(LAST_ACQUIRE_TYPE));

		if (!pLastType)
		{
			break;
		}

		ulSharedFlag=(ULONG_PTR)TlsGetValue(pRwLock->ulTlsForReadIndex);

		ulExclusiveFlag=(ULONG_PTR)TlsGetValue(pRwLock->ulTlsForWriteIndex);

		//先判断当前线程的读写锁状态

		if (ulSharedFlag!=0 &&
			ulSharedFlag!=1)//本线程之前占用了读锁,失败返回
		{
			bIsAcquired=FALSE;

			break;
		}

		if (ulExclusiveFlag!=0 &&
			ulExclusiveFlag!=1)//本线程之前占用写锁
		{
			if(!TlsSetValue(pRwLock->ulTlsForWriteIndex,(PVOID)(ulExclusiveFlag+1)))
			{
				bIsAcquired=FALSE;

				break;
			}

			EnterCriticalSection(&pRwLock->ExclusiveLock);//增加写锁引用

			pLastType->bIsAcquireRead=FALSE;

			pLastType->pPre=pRwLock->pLastAcquireType;

			pRwLock->pLastAcquireType=pLastType;

			pLastType=NULL;

			break;
		}



		//接下来判断其它线程占有读写锁的状态


		//以下一行代码主要这样几个目的:

		//1、若其它线程不占有写锁,那这行代码使得后续企图获取写锁的线程阻塞在这个锁里
		//2、使得那些不占有读锁的线程,企图获取读锁时,阻塞在这个锁里,达到写优先的目的
		//3、占有写锁
		//4、因为Try类型,所以如果失败就返回,不阻塞
		bIsAcquired=TryEnterCriticalSection(&pRwLock->ExclusiveLock);

		if (!bIsAcquired)//其它线程占用写锁,失败返回
		{
			break;
		}

		bIsAcquired=TryEnterCriticalSection(&pRwLock->ShareReaderCountLock);

		if (!bIsAcquired)
		{
			LeaveCriticalSection(&pRwLock->ExclusiveLock);

			break;
		}

		if (pRwLock->iNowReaderCount>0)//其它线程占有读锁,失败返回
		{
			LeaveCriticalSection(&pRwLock->ShareReaderCountLock);

			LeaveCriticalSection(&pRwLock->ExclusiveLock);

			bIsAcquired=FALSE;

			break;
		}else
		{
			LeaveCriticalSection(&pRwLock->ShareReaderCountLock);//释放掉辅助锁,保留写锁
		}


		//没有任何线程占用读锁或者写锁

		ulExclusiveFlag=1;//初始化当前线程占有写锁的标记为首次占用

		if(!TlsSetValue(pRwLock->ulTlsForWriteIndex,(PVOID)(ulExclusiveFlag+1)))
		{
			LeaveCriticalSection(&pRwLock->ExclusiveLock);//失败,释放掉写锁

			bIsAcquired=FALSE;

			break;
		}

		pLastType->bIsAcquireRead=FALSE;

		pLastType->pPre=pRwLock->pLastAcquireType;

		pRwLock->pLastAcquireType=pLastType;

		pLastType=NULL;

	} while (FALSE);


	if (pLastType)
	{
		free(pLastType);

		pLastType=NULL;
	}

	return bIsAcquired;
}


VOID __stdcall ReleaseExclusiveLock(PVOID pLock)
{
	PRW_LOCK pRwLock=(PRW_LOCK)pLock;

	ULONG_PTR ulExclusiveFlag=0;

	ULONG_PTR ulSharedFlag=0;

	ulExclusiveFlag=(ULONG_PTR)TlsGetValue(pRwLock->ulTlsForWriteIndex);

	if (ulExclusiveFlag<=1)
	{
		__debugbreak();
	}

	if(!TlsSetValue(pRwLock->ulTlsForWriteIndex,(PVOID)(ulExclusiveFlag-1)))
	{
		__debugbreak();
	}

	LeaveCriticalSection(&pRwLock->ExclusiveLock);
}

VOID __stdcall ReleaseLock(PVOID pLock)
{
	PRW_LOCK pRwLock=(PRW_LOCK)pLock;

	PLAST_ACQUIRE_TYPE pTmpLastType=NULL;

	while(InterlockedCompareExchange(&pRwLock->lNowIsRelease,1,0)!=0)
	{
		Sleep(0);
	}

	pTmpLastType=pRwLock->pLastAcquireType;

	if (!pTmpLastType)
	{
		__debugbreak();
	}

	pRwLock->pLastAcquireType=pTmpLastType->pPre;

	InterlockedExchange(&pRwLock->lNowIsRelease,0);

	if (pTmpLastType->bIsAcquireRead)
	{
		ReleaseShareLock(pRwLock);

	}else
	{
		ReleaseExclusiveLock(pRwLock);
	}

	free(pTmpLastType);

	pTmpLastType=NULL;
}

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 0
支持
分享
最新回复 (46)
雪    币: 65
活跃值: (112)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
2
具体测试用例类似这样:

先申明一个全局变量:

RW_LOCK_TYPE g_RwLock;

需要加读锁时,直接申明一个读锁临时变量:RLock ReadLock(g_RwLock);
在ReadLock这个变量的有效范围内,读锁有效,不需要专门解锁。

需要加写锁时,直接申明一个写锁临时变量:WLock WriteLock(g_RwLock);
在WriteLock这个变量的有效范围内,写锁有效,不需要专门解锁。

支持N次重入,支持占有写锁的线程继续去占有读锁(反过来不允许),写优先。

不支持占有写锁的线程将写锁转换为读锁(目前暂时没这个需要)。
2014-1-20 12:25
0
雪    币: 209
活跃值: (143)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
有时间看看。

不过我认为这个锁实现的略复杂了,篇幅太长。
2014-1-20 12:45
0
雪    币: 202
活跃值: (46)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
读写锁,不是简单的互斥加锁。
2014-1-20 12:51
0
雪    币: 65
活跃值: (112)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
5
本身就不是简单的互斥加锁。。。所以代码才显得有些复杂。

不然就不会取个“读写锁 ”的名字了
2014-1-20 12:56
0
雪    币: 209
活跃值: (143)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
我之前做过读写锁,代码不超过200行,所以才说篇幅略长。
2014-1-20 13:02
0
雪    币: 209
活跃值: (143)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
另,什么场景下会有获取写锁后不释放就再尝试获取读锁的需求?
2014-1-20 13:03
0
雪    币: 65
活跃值: (112)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
8
你写了一个函数,里面要获取写锁。
然后你去调用你的同事写的一个函数,这个函数需要保证在读数据时,是没别人写数据的,所以默认加了读锁,且这个函数有两种调用方式:
1、最顶层直接调用,当然没问题。
2、被你加了写锁的那个函数调用。这时如果不支持已经在持有写锁的情况下再次去获取读锁,是要死锁的。难不成这个情况。。。你要求你的同事改函数原型,加个参数告诉内部逻辑是不是要获取读锁?
2014-1-20 13:13
0
雪    币: 65
活跃值: (112)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
9
呵呵

你试下那个是不是支持这么多特性就知道究竟需不需要这么长了:
1、只要线程顶层调用的是写锁,后续这个线程里面必须要可以反复多次调用读锁和写锁不死锁
2、写优先
2014-1-20 13:15
0
雪    币: 209
活跃值: (143)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
嵌套支持先写后读,支持先写后写,支持先读后读,不支持先读后写,也不支持最上层是写里面任意读写

支持写优先和非写优先配置
支持try enter的时候设置超时时间
2014-1-20 13:37
0
雪    币: 65
活跃值: (112)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
11
所以嘛,我这个支持了除了你说的支持的外,你不支持的那些情况我这里也有支持,之所以有那些在你口中的“复杂”,也就是因为要支持这些你不支持的,否则这些逻辑很多都不需要。

还有,你的写优先哪里体现出来的
2014-1-20 14:09
0
雪    币: 357
活跃值: (3378)
能力值: ( LV3,RANK:25 )
在线值:
发帖
回帖
粉丝
12
TlsAlloc() 只有64个槽
2014-1-20 14:16
0
雪    币: 65
活跃值: (112)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
13
那也就是限定了我的测试样例中说的那个全局变量,也就是锁变量,在一个进程范围内最多只可以有32个

这个我想多数时候都够了

而且即使真的出现这个情况,也是有其它方法可以解决的,这个不是什么严重问题
2014-1-20 14:20
0
雪    币: 209
活跃值: (143)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
你这个支持超时吗
2014-1-20 14:28
0
雪    币: 65
活跃值: (112)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
15
超时的逻辑处理起来到不是太复杂
我只要把CRITICAL_SECTION的那几个变量全换成内核事件对象,比如mutex,EVENT等等,就能支持超时处理。你可以看到我那个try函数里是有失败后的处理的,如果换成支持try和超时的内核对象,比如mutex,就很容易做出支持超时的接口了,与try那个区别不大(try那几个接口可以看做是超时为0的支持超时的接口)

不过效率考虑(临界区的效率肯定会比内核对象的效率高很多),加上目前多数时候没这个需求,我不想这么做
2014-1-20 14:30
0
雪    币: 65
活跃值: (112)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
16
实际上只要不考虑调用顺序的N种排列组合,读写锁确实几十行一百来行代码能搞定

我最开始的版本没考虑这么复杂的问题,所以就确实只有一百行代码左右
2014-1-20 14:36
0
雪    币: 257
活跃值: (67)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
17
这个值得研究一下
2014-1-20 21:20
0
雪    币: 19
活跃值: (67)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
实现的太复杂了吧。
2014-1-20 22:20
0
雪    币: 1708
活跃值: (586)
能力值: ( LV15,RANK:670 )
在线值:
发帖
回帖
粉丝
19
系统不是会扩展的?
找了微软的资料,每个进程最多有 1088 个 tls 槽。
http://msdn.microsoft.com/en-us/library/ms686749.aspx
我试了下 win7 x64 ,控制台空项目,能有 1086  个 tls 槽。
2014-1-25 22:01
0
雪    币: 7309
活跃值: (3788)
能力值: (RANK:1130 )
在线值:
发帖
回帖
粉丝
20
1,实现代码太长,隐藏BUG比较多
2,没有timeout的TryxxxLock是没太大意义
3,不能跨平台
4,win7开始,系统有读写锁实现
5,boost库里面也有读写锁
2014-1-26 10:13
0
雪    币: 209
活跃值: (143)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
The constant TLS_MINIMUM_AVAILABLE defines the minimum number of TLS indexes available in each process. This minimum is guaranteed to be at least 64 for all systems. The maximum number of indexes per process is 1,088.

每个进程可以有64个有保证的槽,64个以内可以保证被开辟,64个之外就要看内存使用情况了,但总数绝不会超过1088。原来是这样。
2014-1-26 10:24
0
雪    币: 65
活跃值: (112)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
22
第一,代码长短的问题,这个实际上没有个绝对的标准,你说长了就长了吧;关于隐藏bug的,麻烦不吝赐教随便点出几个。
第二,这个之前就说过了,只需要把CRITICAL_SECTION改成支持超时的锁对象,比如event等等,就能有超时机制。
第三,锁机制本身就是与进线程以及操作系统、硬件密切相关的,那些所谓夸平台的锁,无非是写了两份代码,保持了对外接口不变而已。
第四,nt6系统那个锁重入有问题,n久前就试过了
第五,booost那个,不支持锁重入,就是因为这个问题,导致产品出了问题,所以才要自己写。另外有个支持重入的锁,那个没法用在读写锁环境里
2014-1-26 11:17
0
雪    币: 130
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
锁应用timeout是因为线程半路死掉吗换个互斥体 不过也影响性能
2014-1-26 11:20
0
雪    币: 65
活跃值: (112)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
24
与线程半路挂掉没关系,这个与那些带超时机制的普通锁的原因一样,没什么好说的
2014-1-26 11:49
0
雪    币: 496
活跃值: (286)
能力值: ( LV13,RANK:400 )
在线值:
发帖
回帖
粉丝
25
代码不错,但是缺少亮点,锁本来就是一个吃性能的同步工具,楼上有人说复杂也是没错的,个人认为读写锁这种工具在实现其基本功能的前提下,为了保证性能,应该尽量越简洁越好。
一个临界区就够重了,你还用了两个,不要把临界区看得太过高效,对于线程密集型程序来说,临界区的效率随着资源征用的激烈程度而下降,当资源的平均独占时间超过了临界区内的自旋锁的初始值时,临界区的性能就比内核对象还差了,而且会增加功耗。
建议,精简锁的结构,该合并的成员就合并,比如最终合并成一个64位的整数,然后去除临界区,改用整形的原子操作以及CAS模式,这样来降低锁自身的资源占用,提高性能,并且增加可移植性,还有你这个TLS用的也是相当复杂并且不具备移植性,在我看来TLS在这里不如不用。
最后一点,您的代码内存泄露了。
总结,一切把简单问题复杂化,并且最终变得你自己都无法掌控的代码,就是垃圾代码啊,楼主加油,手机打字,不用谢,请叫我红领巾。
2014-1-26 12:39
0
游客
登录 | 注册 方可回帖
返回
//