수동 리셋 이벤트

 

앞서 살펴봤던 자동 리셋 이벤트는 대기 상태를 풀 때 자동으로 대기함수에서 이벤트를 비신호 상태로 만들어주었다.

하나의 스레드만 사용할 경우에는 자동 리셋 이벤트를 사용하는 것이 훨씬 편리하며 논리적으로도 문제가 없다.

 

그러나 하나의 이벤트를 여러 개의 스레드가 기다리고 있는 경우에는 이야기가 달라진다.
예를 들어 계산 스레드가 계산을 완료하고 나서 진행될 작업이 3개가 있고 이 3개 스레드의 대기함수는 계산이 끝나길 기다리고 있다.

그리고 계산이 끝나게 되면 3개 스레드는 일제히 작업을 해야 하는데 자동 리셋 이벤트는 이것이 불가능하다.


이유는 계산 스레드에서 계산이 끝나고 SetEvent를 통해 Event를 대기 상태로 만들고 나면 제일 먼저 스위칭되는 스레드의 대기 함수가

동작하고 이 대기 함수는 Event를 다시 비신호 상태로 만들기 때문에 나머지 2개 스레드는 계산 스레드에서 계산이 끝났는지에 대한 여부를

알 수 없게 된다.


즉, 자동 리셋 이벤트는 단 하나의 스레드만 신호를 받아 처리할 수 있는 구조이다.

 

이에 반해 수동 리셋 이벤트는 대기 함수가 리턴될 때 신호상태를 그대로 유지하며 ResetEvent 함수를 통해 비신호 상태로 변경시킬 수 있다.

 

하기 예제 코드를 참고해보자.

 

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
HANDLE hEvent;
 
DWORD WINAPI ThreadSend(LPVOID prc)
{
    WaitForSingleObject(hEvent, INFINITE);
    HDC hdc = GetDC(hWndMain);
    TextOut(hdc, 210100"전송완료"8);
    ReleaseDC(hWndMain, hdc);
 
    return 0;
}
 
DWORD WINAPI ThreadSave(LPVOID prc)
{
    WaitForSingleObject(hEvent, INFINITE);
    HDC hdc = GetDC(hWndMain);
    TextOut(hdc, 110100"저장완료"8);
    ReleaseDC(hWndMain, hdc);
 
    return 0;
}
 
DWORD WINAPI ThreadPrint(LPVOID prc)
{
    WaitForSingleObject(hEvent, INFINITE);
    HDC hdc = GetDC(hWndMain);
    TextOut(hdc, 10100"인쇄완료"8);
    ReleaseDC(hWndMain, hdc);
 
    return 0;
}
 
DWORD WINAPI ThreadCalc(LPVOID prc)
{
    HDC hdc = GetDC(hWndMain);
    for (int i=0; i<10++i)
    {
        TextOut(hdc, 1050"계산중"6);
        GdiFlush();
        Sleep(300);
        TextOut(hdc, 1050"기다려",6);
        GdiFlush();
        Sleep(300);
    }
    TextOut(hdc, 1050"계산완료"8);
    ReleaseDC(hWndMain, hdc);
    SetEvent(hEvent); //이벤트 신호 상태 변경.
 
    return 0;
}
 
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    DWORD ThreadID;
    TCHAR *Mes = "마우스 왼쪽 버튼을 누르면 계산을 시작합니다.";
 
    switch(iMessage) 
    {
    case WM_CREATE:
        hWndMain = hWnd;
        hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); //비신호상태 수동 리셋 이벤트 생성.
        return TRUE;
    case WM_LBUTTONDOWN:
        InvalidateRect(hWnd, NULL, TRUE);
        ResetEvent(hEvent); //이벤트 비신호 상태 변경.
        CloseHandle(CreateThread(NULL,0,ThreadCalc,NULL,0,&ThreadID));
        CloseHandle(CreateThread(NULL,0,ThreadPrint,NULL,0,&ThreadID));
        CloseHandle(CreateThread(NULL,0,ThreadSave,NULL,0,&ThreadID));
        CloseHandle(CreateThread(NULL,0,ThreadSend,NULL,0,&ThreadID));
        return 0;
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        TextOut(hdc, 1010, Mes, lstrlen(Mes));
        EndPaint(hWnd, &ps);
        return 0;
    case WM_DESTROY:
        CloseHandle(hEvent);
        PostQuitMessage(0);
        return 0;
    }
    return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}
cs

 

하나의 계산 스레드가 동작이 완료되면 인쇄, 저장, 전송 세 개의 작업 스레드가 실행되는 구조이다.
이벤트 객체를 생성할 때 두 번째 인수를 TRUE로 설정하여 수동 리셋 이벤트가 되도록 한다.

계산 스레드가 동작이 완료될 때까지는 Event 객체가 비신호 상태이므로 인쇄, 저장, 전송 스레드의 대기 함수는 대기 상태로 있고

계산 스레드에서 SetEvent를 거치는 순간부터 일제히 인쇄, 저장, 전송 기능이 실행되게 된다.

 

 

 

+ Recent posts