vista, windows 2008 이상에서 사용가능한 유저 모드 동기화 객체로 속도에 최적화 되어있으며, 메모리 사용량도 매우 낮다. 기존 CRITICAL_SECTION 과는 다르게 두가지의 잠금 모드를 제공한다.

  • Shared mode
    • 공유 리소스에 대해 읽기 전용으로 접근하는 경우 사용하는 잠금 모드이며, 동시에 여러 스레드에서 읽기 모드로 잠금이 가능하다. 만약 읽기 작업이 쓰기 작업보다 많은 경우 CRITICAL_SECTION 에 비해 동시성 처리 능력이 상승한다.
  • Exclusive mode
    • 기존 CRITICAL_SECTION 의 잠금 모드와 동일한 잠금 모드로서, 하나의 스레드만이 접근 가능하다. 쓰기 작업에 사용한다.

하나의 SWR 객체를 통해 두가지 잠금 모드로 사용이 가능하며, 소유권 획득은 요청 순서를 따르지 않는다. 하지만 공정하다(?) 라고 한다. SWR 객체 크기는 포인터 사이즈이며, 이로 인한 잠금 상태를 업데이트 하는 속도가 빠르다라는 장점이 존재한다. 하지만 매우 작은 상태 정보만 저장이 가능함에 따른 재귀적으로 잠금을 획득할 수 없는 단점이 존재한다. 위의 단점때문에 단일로 사용하기는 힘들고 랩핑을 통해 중복적으로 락을 획득하는 경우를 방지해야한다. Exclusive mode 로 하나의 스레드에서 두번 lock 을 걸면 두번째 lock 획득 시도에서 무한 대기상태로 진입한다. (추가로 Shared mode 로 잠금을 획득한 상태에서 Exclusive mode 로 업그레이드는 불가능하다.) 관련 함수는 다음과 같다.

    • SWR 객체에 대해 Exclusive mode 로 잠금을 획득한다.
    • SWR 객체에 대해 Shared mode 로 잠금을 획득한다.
    • SWR 객체를 초기화 한다.
    • Exclusive mode 로 잠금된 SWR 객체를 잠금 해제한다.
    • Shared mode 로 잠금된 SWR 객체를 잠금 해제한다.
    • SWR 객체에 대해 Exclusive mode 로 호출 시점에 잠금 획득이 가능한 경우 true 를 불가능한 경우 false 를 반환한다.
    • SWR 잠금 기능이 최초 추가되었을 때에는 Try 함수가 존재하지 않았었다.
    • SWR 객체에 대해 Shared mode 로 호출 시점에 잠금 획득이 가능한 경우 true 를 불가능한 경우 false 를 반환한다.
    • SWR 잠금 기능이 최초 추가되었을 때에는 Try 함수가 존재하지 않았었다.

CRITICAL_SECTION 과는 다르게 메모리 해제 관련 작업이 존재하지 않는다. (이와는 별도로 condition variable 과 연동 가능한 함수가 존재하지만 여기에서는 다루지 않는다.) i5-760 에서 단순 테스트 한 결과를 올림.

DWORD gTotalTime = 0;

SRWLOCK gSRWLock;
CRITICAL_SECTION gCS; 

unsigned __stdcall ThreadFun(void* pParm)
{
	for(size_t ii = 0; ii < 1000000; ++ii)
	{
		#if defined(SRW_LOCK)
		::AcquireSRWLockExclusive(&gSRWLock);
		#else
		::EnterCriticalSection(&gCS);
		#endif         

		++ gValue;         

		#if defined(SRW_LOCK)
		::ReleaseSRWLockExclusive(&gSRWLock);
		#else
		::LeaveCriticalSection(&gCS);
		#endif
	}     

	return 0;
}

INT    _tmain()
{
	typedef std::array
	TThreadHndList;     

	::InitializeCriticalSectionAndSpinCount(&gCS, 0x8000);
	::InitializeSRWLock(&gSRWLock);     

	TThreadHndList    aHndList;    

	std::fill(aHndList.begin(), aHndList.end(), static_cast(NULL));     

	for(size_t ii = 0; ii < ahndlist.size(); ++ii)
	{
		unsigned aThreadID;
		aHndList[ii]= reinterpret_cast<HANDLE>(::_beginthreadex(NULL, 0, ThreadFun, NULL, CREATE_SUSPENDED, &aThreadID));
	}     

	gTotalTime = ::GetTickCount();    

	std::for_each(aHndList.begin(), aHndList.end(), std::ptr_fun(ResumeThread));     

	DWORD aRV = ::WaitForMultipleObjects(aHndList.size(), aHndList.data(), TRUE, INFINITE);    

	gTotalTime = ::GetTickCount() - gTotalTime;    

	std::cout << "gVal : " << gValue << "\tTotal Time : " << gTotalTime << std::endl;     

	std::for_each(aHndList.begin(), aHndList.end(), std::ptr_fun(CloseHandle));     

	::DeleteCriticalSection(&gCS);
	return 0;
}
스레드 개수 Critical Section Slim Reader Writer locks
4 140 / 686 124
8 281 / 1300 250
* Critical Section 왼쪽은 단순 InitializeCriticalSection 통해 테스트 한 결과 우측의 결과는 InitializeCriticalSectionAndSpinCount 를 통해 테스트 한 결과임.
* 물론 테스트 환경이 극단적으로 모든 스레드들이 동기화 객체에 lock, 변수 변경, unlock 하는 환경인지라 특정 상황에(spin count) 불리할 수 있다.

Post to Twitter