동기화 객체.

 

동기화 객체란 말 그대로 동기화에 사용되는 객체이다. 프로세스, 스레드처럼 커널 객체이며 프로세스 한정적인 핸들을 가진다.

앞에서 살펴본 크리티컬 섹션은 구조체일 뿐이지 커널 객체는 아니다.

 

동기화 객체는 크리티컬 섹션보다 느리기는 하지만 훨씬 더 복잡한 동기화에 사용될 수 있다.

동기화 객체는 일정 시점에서 다음 두 가지 상태 중 한 상태를 가진다.

 

 - 신호 상태 : 스레드의 실행을 허가하는 상태. 신호상태의 동기화 객체를 가진 스레드는 계속 실행할 수 있다.

                  신호등의 파란불에 비유할 수 있다.

 

 - 비신호 상태 : 스레드의 실행을 허가하지 않는 상태. 신호상태가 될 때까지 스레드는 블록된다.

                     신호등의 빨간불에 비유할 수 있다.

 

동기화 객체는 대기 함수와 함께 사용되는데 대기 함수(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(NULL0, ThreadFunc1, NULL0&ThreadID);
        CloseHandle(hThread);
        hThread = CreateThread(NULL0, ThreadFunc2, NULL0&ThreadID);
        CloseHandle(hThread);
        return 0;
    case WM_DESTROY:
        CloseHandle(hMutex); //Mutex 파괴.
        PostQuitMessage(0);
        return 0;
    }
    return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}
cs

 

 

+ Recent posts