동기화 객체.
동기화 객체란 말 그대로 동기화에 사용되는 객체이다. 프로세스, 스레드처럼 커널 객체이며 프로세스 한정적인 핸들을 가진다.
앞에서 살펴본 크리티컬 섹션은 구조체일 뿐이지 커널 객체는 아니다.
동기화 객체는 크리티컬 섹션보다 느리기는 하지만 훨씬 더 복잡한 동기화에 사용될 수 있다.
동기화 객체는 일정 시점에서 다음 두 가지 상태 중 한 상태를 가진다.
- 신호 상태 : 스레드의 실행을 허가하는 상태. 신호상태의 동기화 객체를 가진 스레드는 계속 실행할 수 있다.
신호등의 파란불에 비유할 수 있다.
- 비신호 상태 : 스레드의 실행을 허가하지 않는 상태. 신호상태가 될 때까지 스레드는 블록된다.
신호등의 빨간불에 비유할 수 있다.
동기화 객체는 대기 함수와 함께 사용되는데 대기 함수(Wait Function)는 일정한 조건에 따라 스레드의 실행을 블록하거나
실행을 허가하는 함수이다. 여기서 일정한 조건이란 주로 동기화 객체의 신호 여부가 된다.
<대표적인 대기 함수>
1 |
DWORD WINAPI WaitForSingleObject(__in HANDLE hHandle, __in DWORD dwMilliseconds); |
cs |
이 함수는 hHandle이 지정하는 하나의 동기화 객체가 신호상태가 되기를 기다린다.
dwMilliseconds 인수는 타임 아웃 시간을 1/1000초 단위로 지정하는데 이 시간이 경과하면 설사 동기화 객체가
비신호상태이더라도 즉시 리턴함으로써 무한 대기를 방지한다. 대신 dwMilliseconds를 INFINITE로 지정하면
신호상태가 될 때까지 무한정 대기한다.
WaitForSingleObject 함수는 동기화 객체가 신호상태가 되거나 타임 아웃 시간이 경과할 때까지 스레드의 실행을
블록하는 역할을 한다고 할 수 있다.
이 함수의 리턴값을 검사해 보면 어떤 이유로 대기상태를 종료했는지를 알 수 있다.
- WAIT_OBJECT_0 : hHandle 객체가 신호상태가 되었다.
- WAIT_TIMEOUT : 타임 아웃 시간이 경과하였다.
- WAIT_ABANDONED : 포기된 뮤텍스.
WaitForSingleObject 함수는 리턴하기 전에 hHandle 동기화 객체의 상태를 변경한다. (신호상태 → 비신호상태)
그래서 일단 한 스레드가 동기화 객체를 소유하면 동기화 객체는 비신호상태가 되므로 다른 스레드가 이 객체를
중복하여 소유하지 못한다.
1) 뮤텍스.
뮤텍스는 크리티컬 섹션과 여러 가지 면에서 비슷하며 크리티컬 섹션이 쓰이는 곳에 대신 사용될 수 있다.
프로세스간에도 사용할 수 있다는 장점이 있으나 그만큼 속도는 느리다. 그러나 같은 프로세스내의 스레드끼리만
동기화한다면 속도 차이는 크리티컬 섹션과 거의 없다.
Mutex는 Mutual Exclusion의 준말인데 두 스레드가 동시에 소유할 수 없다는 뜻이며 오직 한 스레드에 의해서만
소유될 수 있으며 어떤 스레드에게 소유되면 뮤텍스는 비신호상태가 된다.
반대로 어떠한 스레드에서도 소유하지 않고 있으면 신호상태인 것이다.
1
2 |
HANDLE WINAPI CreateMutex(__in_opt LPSECURITY_ATTRIBUTES lpMutexAttributes, __in BOOL bInitialOwner,
__in_opt LPCSTR lpName); |
cs |
우선 CreateMutex함수를 통해서 Mutext를 생성 한 후 그 핸들을 리턴받는다.
- 첫 번째 인수는 보안속성을 지정하는데 대개 NULL로 준다.
- 두 번째 인수는 뮤텍스를 생성함과 동시에 소유할 것인지를 지정하는데 이 값이 TRUE이면 이 함수를 호출한 스레드가 먼저
뮤텍스를 소유한 상태에서 동작한다. FALSE인 경우는 최초로 대기 함수를 호출하는 함수가 이 뮤텍스를 소유하는데 스레드에서
뮤텍스를 생성할 때는 통상 FALSE로 준다.
- 세 번째 인수는 뮤텍스의 이름을 지정하는 문자열이다. 프로세스 간에 뮤텍스가 공유될 때 이 이름을 이용한다.
커널 객체들은 이런 식으로 문자열로 된 이름을 가짐으로써 공유가 가능하며 다른 프로세스에서 뮤텍스의 이름만 알면 OpenMutex
함수로 뮤텍스의 핸들을 얻을 수 있다. 또는 같은 이름으로 CreateMutex를 한 번 더 호출해도 된다.
1 |
HANDLE WINAPI OpenMutex(__in DWORD dwDesiredAccess, __in BOOL bInheritHandle, __in LPCSTR lpName); |
cs |
CreateMutex, OpenMutex 함수가 지정하는 뮤텍스의 이름은 시스템 전역적으로 유일하게 하나의 뮤텍스를 가리킨다.
(즉, 1 PC에 동일한 이름의 서로 다른 뮤텍스는 존재할 수 없다는 것임.)
만약 한 프로세스 내에서만 사용한다면 뮤텍스의 이름을 줄 필요없이 세 번째 인자에 NULL을 주면된다.
뮤텍스를 파괴할 때는 모든 커널 객체와 마찬가지로 CloseHandle 함수를 사용한다.
CloseHandle은 뮤텍스 핸들을 닫으며 만약 이 핸들이 대상 뮤텍스를 가리키는 마지막 핸들이라면 뮤텍스 객체도 파괴된다.
뮤텍스는 스스로 카운트를 관리하며 모든 핸들이 닫힐 때 객체도 소멸된다. (스마트 포인터랑 같은 개념이라고 생각하면 됨.)
또한 비신호 상태의 뮤텍스를 다시 신호 상태로 변경할 때는 하기 함수를 사용한다.
1 |
BOOL WINAPI ReleaseMutex(__in HANDLE hMutex); |
cs |
<뮤텍스 사용 예제>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 |
int X;
HANDLE hMutex;
DWORD WINAPI ThreadFunc1(LPVOID temp)
{
HDC hdc;
hdc = GetDC(hWndMain);
for (int i=0; i<100; ++i)
{
WaitForSingleObject(hMutex, INFINITE); //Mutex가 신호 상태가 될 때가지 무한정 대기.
//Mutex가 신호 상태가 되었다면 대기함수를 지나고
//Mutex는 비신호 상태로 변경된다.
X = 100;
Sleep(1);
TextOut(hdc, X, 100, "고양이", 6);
ReleaseMutex(hMutex); //대기 함수가 변경한 뮤텍스의 비신호 상태를
//다른 Thread의 대기 함수 또한 실행될 수 있도록
//신호상태로 변경한다.
}
ReleaseDC(hWndMain, hdc);
return 0;
}
DWORD WINAPI ThreadFunc2(LPVOID temp)
{
HDC hdc;
hdc = GetDC(hWndMain);
for (int i=0; i<100; ++i)
{
WaitForSingleObject(hMutex, INFINITE); //Mutex가 신호 상태가 될 때가지 무한정 대기.
X = 200;
Sleep(1);
TextOut(hdc, X, 200, "강아지", 6);
ReleaseMutex(hMutex); //신호 상태로 변경.
}
ReleaseDC(hWndMain, hdc);
return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
HANDLE hThread;
DWORD ThreadID;
switch(iMessage)
{
case WM_CREATE:
hMutex = CreateMutex(NULL, FALSE, NULL); //Mutex 생성.
hWndMain = hWnd;
return 0;
case WM_LBUTTONDOWN:
hThread = CreateThread(NULL, 0, ThreadFunc1, NULL, 0, &ThreadID);
CloseHandle(hThread);
hThread = CreateThread(NULL, 0, ThreadFunc2, NULL, 0, &ThreadID);
CloseHandle(hThread);
return 0;
case WM_DESTROY:
CloseHandle(hMutex); //Mutex 파괴.
PostQuitMessage(0);
return 0;
}
return(DefWindowProc(hWnd,iMessage,wParam,lParam));
} |
cs |
'Programming > Thread' 카테고리의 다른 글
[API] Thread 8 (이벤트 - 자동 리셋 이벤트) (0) | 2017.09.11 |
---|---|
[API] Thread 6 (동기화 - 크리티컬 섹션) (0) | 2017.08.30 |
[API] Thread 5 (스케줄링) (0) | 2017.08.30 |
[API] Thread 4 (UI 스레드) (0) | 2017.08.30 |
[API] Thread 3 (Thread 관리) (0) | 2017.08.29 |