728x90

세마포어와 뮤텍스 비교

- 세마포어 동기화 기법 중 binary 세마포어를 뮤텍스라 함

- 세마포어의 일부로 뮤텍스가 존재

- 윈도우에서는 각각을 기법으로 가져와서 사용

- 임계 영역에 접근하는 키가 여러개면 세마포어 하나면 뮤텍스

- 세마포어는 임계 영역에 들어오는 쓰레드의 수를 제어할 수 있음(키의 갯수에 따라 달라짐)

- 뮤텍스는 키가 하나이기 때문에 임계 영역에 하나의 쓰레드만 들어올 수 있음

 

뮤텍스의 생성 및 실행

- CreateMutex : 열쇠를 생성하는 함수

    -> 커널 오브젝트 생성을 동반하기 때문에 핸들도 반환되고 핸들 테이블의 상속을 결정하는 보안 설정을 함

    -> 소유자를 지정하면 열쇠를 만든사람이 소유하는지 열쇠를 만든 사람도 소유의 권한을 가지지 않고 누구나 소유할 수 있는 지를 결정

HANDLE CreateMutexA(
  LPSECURITY_ATTRIBUTES lpMutexAttributes,	// 보안 설정
  BOOL                  bInitialOwner,		// 열쇠의 소유
  LPCSTR                lpName				// 뮤텍스 이름
);
DWORD WaitForSingleObject(
  HANDLE hHandle,
  DWORD  dwMilliseconds
);	// 지정한 오브젝트가 Signaled 상태가 되기를 기다리는 함수

BOOL ReleaseMutex(
  HANDLE hMutex
);

- WaitForSingleObject(), ReleasMutex()를 통해서 뮤텍스(열쇠)를 획득하고 반납

- 뮤텍스를 커널 오브젝트라고 생각하면 WaitForSingleObject()가 빠져나오기 위해선 뮤텍스가 Signaled 상태가 되야 함

    -> ReleaseMutex()는 뮤텍스를 Signaled 상태가 되게 하는 함수

    -> WaitForSingleObject()는 뮤텍스를 Non-Signaled 상태로 바꾸게 되는 함수

- 쓰레드가 임계 영역으로 들어가기 전 뮤텍스를 얻기 위해 뮤텍스 핸들을 인자로 전달하면서 WaitForSingleObject() 함수를 호출하고 뮤텍스가 획득 가능 상태라면 Signaled 상태에 있고 뮤텍스를 획득하면서 임계 영역에 진입

- WaitForSingleObject 함수는 매개변수로 호출한 오브젝트가 Signaled 상태가 되기를 기다리기 때문에 다른 쓰레드는 임계 영역으로의 진입이 불가능

- 임계 영역에 진입한 쓰레드가 빠져나오면서 ReleaseMutex 함수를 호출하면 뮤텍스는 Signaled 상태가 되어 다른 쓰레드가 획득할 수 있게 되어 다른 쓰레드의 진입을 허용함

#include <stdio.h>
#include <Windows.h>
#include <tchar.h>
#include <process.h>

#define NUM_OF_GATE	6

LONG gTotalCount = 0;		// 둘 이상의 쓰레드가 접근하는 변수

// CRITICAL_SECTION cSection;
HANDLE hMutex;

void IncreaseCount()
{
	// EnterCriticalSection(&cSection);		// 임계 영역 시작
	WaitForSingleObject(hMutex, INFINITE);

	gTotalCount++;

	// LeaveCriticalSection(&cSection);		// 임계 영역 끝
	ReleaseMutex(hMutex);
}

unsigned int WINAPI ThreadProc(LPVOID lpParam)
{
	for (DWORD i = 0; i < 1000; i++)
	{
		IncreaseCount();
	}
	return 0;
}

int _tmain(int argc, TCHAR* argv[])
{
	DWORD	dwThreadId[NUM_OF_GATE];
	HANDLE	hThread[NUM_OF_GATE];

	// InitializeCriticalSection(&cSection);
	hMutex = CreateMutex(NULL, FALSE, NULL);

	if (hMutex == NULL)
	{
		_tprintf(_T("CreateMutex Error : %d\n"), GetLastError());
	}

	for (DWORD i = 0; i < NUM_OF_GATE; i++)
	{
		hThread[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadProc, NULL, CREATE_SUSPENDED, (unsigned*)& dwThreadId[i]);
		if (hThread[i] == NULL)
		{
			_tprintf(_T("Thread create error\n"));
			return -1;
		} // 쓰레드를 생성하지 못하면 NULL이 반환
	}

	for (DWORD i = 0; i < NUM_OF_GATE; i++)
	{
		ResumeThread(hThread[i]);
	}

	WaitForMultipleObjects(NUM_OF_GATE, hThread, TRUE, INFINITE);	// 쓰레드가 종료될 때까지 대기

	_tprintf(_T("total count : %d \n"), gTotalCount);

	for (DWORD i = 0; i < NUM_OF_GATE; i++)
	{
		CloseHandle(hThread[i]);
	}

	// DeleteCriticalSection(&cSection);
	CloseHandle(hMutex);

	return 0;
}

 

세마포어의 생성 및 실행

HANDLE CreateSemaphoreA(
  LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
  LONG                  lInitialCount,		
  // 세마포어가 가지는 열쇠의 개수(값) = 임계 영역에 동시 접근할 수 있는 쓰레드 수
  LONG                  lMaximumCount,	// 세마포어가 가질 수 있는 최대 열쇠의 수 (InitialCount 이상)
  LPCSTR                lpName
);
BOOL ReleaseSemaphore(
  HANDLE hSemaphore,
  LONG   lReleaseCount,
  LPLONG lpPreviousCount
);

- WaitForSingleObject, ReleaseSemaphore 함수가 호출되면 세마포어 카운트가 감소 / 증가됨

- 세마포어 카운트 값이 0이 되지 않는 이상 Non-signaled 상태가 되지 않음

- Signaled 상태로 계속 있기 때문에 WaitForSingleObject를 호출해도 임계 영역에 다른 쓰레드도 진입 가능

#include <stdio.h>
#include <tchar.h>
#include <time.h>
#include <windows.h>
#include <process.h>

#define NUM_OF_CUSTOMER 20
#define RANGE_MIN 10
#define RANGE_MAX (30-RANGE_MIN)
#define TABLE_CNT 10

HANDLE hSemaphore;
DWORD randTimeArr[50];

void TakeMeal(DWORD time)
{
	WaitForSingleObject(hSemaphore, INFINITE);
	_tprintf(_T("Enter Customer %d ~ \n"), GetCurrentThreadId());

	_tprintf(_T("Customer %d having launch ~ \n"), GetCurrentThreadId());
	Sleep(1000 * time);

	ReleaseSemaphore(hSemaphore, 1, NULL);
	_tprintf(_T("Out Customer %d ~ \n\n"), GetCurrentThreadId());
}

unsigned int WINAPI ThreadProc(LPVOID lpParam)
{
	TakeMeal((DWORD)lpParam);
	return 0;
}

int _tmain(int argc, TCHAR* argv[])
{
	DWORD dwThreadIDs[NUM_OF_CUSTOMER];
	HANDLE hThreads[NUM_OF_CUSTOMER];

	srand((unsigned)time(NULL));

	for (int i = 0; i < NUM_OF_CUSTOMER; i++)
	{
		randTimeArr[i] = DWORD((double)rand() / (double)RAND_MAX * RANGE_MAX + RANGE_MIN);
	}

	hSemaphore = CreateSemaphore(NULL, TABLE_CNT, TABLE_CNT, NULL);
	if (hSemaphore == NULL)
	{
		_tprintf(_T("Create Semaphore error : %d\n"), GetLastError());
	}
	for (int i = 0; i < NUM_OF_CUSTOMER; i++)
	{
		hThreads[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadProc, (void*)randTimeArr[i], CREATE_SUSPENDED, (unsigned*)& dwThreadIDs[i]);
		if (hThreads[i] == NULL)
		{
			_tprintf(_T("Create Thread error : %d\n"), GetLastError());
		}
	}

	for (int i = 0; i < NUM_OF_CUSTOMER; i++)
	{
		ResumeThread(hThreads[i]);
	}

	WaitForMultipleObjects(NUM_OF_CUSTOMER, hThreads, TRUE, INFINITE);

	_tprintf(_T("----------END----------"));

	for (int i = 0; i < NUM_OF_CUSTOMER; i++)
	{
		CloseHandle(hThreads[i]);
	}

	CloseHandle(hSemaphore);
	return 0;
}

- 생성하는 쓰레드의 개수 : NUM_OF_CUSTOMER

- 임계영역에 한번에 접근할 수 있는 쓰레드의 수 = TABLE_CNT

- 처음에 실행하면 20개중 10개의 쓰레드가 임계 영역에 진입하고 세마포어 카운트가 0이 됨

- 주어진 Sleep 시간 이후 임계 영역을 나오면서 ReleaseSemaphore 함수를 통해 세마포어 카운드를 증가시킴

- 쓰레드가 빠져 나오면 기다리고 있던 쓰레드가 다시 들어가고 세마포어 카운트가 증가

- 이후 모든 쓰레드가 진입 후 나오게 되면서 세마포어 카운트가 다시 TABLE_CNT가 됨

728x90

+ Recent posts