[WinAPI] CriticalSection, 임계영역

이번 글에서는 임계영역에 대해 알아보겠습니다.

임계 영역(Critical Region)병행 처리(Concurrent Processing)에서 공유 자원에 접근하는 부분을 말합니다. 공유 자원은 여러 개의 스레드나 프로세스들이 동시에 접근할 수 있는 자원을 말합니다.

예를 들어 아래와 같이 전역 변수 a에 10을 더해주고 출력하는 쓰레드1이 있다고 가정합니다.

쓰레드1만 실행하는 경우 의도한 대로 10이 더해지고, a를 출력하면 10이 출력될겁니다.
하지만 10을 빼는 쓰레드 2를 쓰레드1과 동시에 실행하면 어떻게 될까요?

위 그림에서 출력만 보면 정상적으로 동작하지 않는다는 것이 한눈에 보입니다.
쓰레드1 입장에서는 a가 0이였고 10을 더했는데 더한 후에도 0으로 출력되고,
쓰레드2 입장에서도 a가 0이였는데 -10을 해도 0으로 출력됩니다.
실행 순서는 순전히 CPU 맘이므로 출력 결과만 보는 개발자 입장에서는 죽을 맛일 겁니다.

이런 경우 공유자원에 여러 쓰레드가 접근하지 못하게 임계 영역을 보호(lock/unlock)하여 정상적으로 수행 시킵니다.

C++에서는 이러한 임계영역을 관리하기 위해 CCriticalSection 클래스를 제공해줍니다.
CCriticalSection 사용법은 간단합니다.

// .h
int a = 0;
CCriticalSection m_cr;

UINT ThreadPlus(LPVOID lpVoid);
UINT ThreadMinus(LPVOID lpVoid);
// .cpp
UINT CMFCApplication1Dlg::ThreadPlus(LPVOID lpVoid)
{
	CMFCApplication1Dlg *pThis = (CMFCApplication1Dlg*)lpVoid;
	while (TRUE)
	{
		pThis->m_cr.Lock();
		TRACE(_T("Thread 1, a: %d\n"), a);
		pThis->a += 10;
		TRACE(_T("Thread 1, a: %d\n"), a);
		pThis->m_cr.Unlock();
		Sleep(1000);
	}
	return 0;
}

UINT CMFCApplication1Dlg::ThreadMinus(LPVOID lpVoid)
{
	CMFCApplication1Dlg *pThis = (CMFCApplication1Dlg*)lpVoid;
	while (TRUE)
	{
		pThis->m_cr.Lock();
		TRACE(_T("Thread 2, a: %d\n"), a);
		pThis->a -= 10;
		TRACE(_T("Thread 2, a: %d\n"), a);
		pThis->m_cr.Unlock();
		Sleep(1000);
	}
	return 0;
}