스레드 관리.

 

실제 스레드는 일정한 백그라운드 작업을 맡아 처리하고 작업이 끝나면 종료되는 것이 일반적이다.

작업 스레드가 백그라운드 작업을 할 때 주 스레드는 작업 스레드를 만들기만 하고 종료 상태에는 별로 관심을 두지 않는 것이

보통이다. 두 스레드(주 스레드와 작업 스레드)는 서로 독립적으로 실행되기 때문이지만 주 스레드는 적어도 작업 스레드가 종료

되었는지의 여부는 주기적으로 조사해 봐야 하는데 이때는 다음 함수가 사용된다.

 

<Thread 종료 여부 확인 함수 - GetExitCodeThread 함수.>

 

1
2
3
4
5
6
7
BOOL
WINAPI
GetExitCodeThread(
    __in  HANDLE hThread,
    __out LPDWORD lpExitCode
    );
 
cs

 

 · Parameter 1 (HANDLE hThread)

 

  - 종료 여부를 확인하고자 하는 작업 스레드의 핸들을 넘긴다.

 

 · Parameter 2 (LPDWORD lpExitCode)

 

  - Parameter 1을 넘기면 이 스레드의 종료 코드를 Parameter 2로 리턴한다.

  - 스레드가 계속 실행중이라면 STILL_ACTIVE 가 리턴된다.

  - 스레드가 종료되었다면 스레드 시작함수(앞의 예제의 ThreadFunc 함수)가 리턴한 값이나 하기에 나오는 Thread 죵료 함수인

    ExitThread 함수를 통해 전달한 종료 코드가 리턴된다.

 

 만약 앞서 만든 예제처럼 작업 스레드가 무한 루프로 돌고 있다고 해도 모든 스레드는 프로세스가 종료되면 강제로 종료되므로

 무한 루프를 도는 스레드를 만들어도 상관없다.

 

때로는 사용자가 직접 작업 중간에 스레드를 종료해야 될 경우도 있다. 스레드를 강제 종료할 때는 하기 두 함수를 사용한다.

 

<Thread 강제 종료 함수>

 

 1) ExitThread 함수.

1
2
3
4
5
VOID
WINAPI
ExitThread(
    __in DWORD dwExitCode
    );
cs

 

 ExitThread는 스레드가 스스로를 종료할 때 사용하는데 인수로 종료 코드를 전달한다.

 종료 코도는 주 스레드에서 GetExitCodeThread 함수로 받을 수 있다. 스레드가 ExitThread를 호출하면 자신의 스택을 해제하고

 연결된 DLL을 모두 분리한 후 스스로 파괴된다.

 

 2) TerminateThread 함수.

1
2
3
4
5
6
BOOL
WINAPI
TerminateThread(
    __in HANDLE hThread,
    __in DWORD dwExitCode
    );
cs

 

 TerminateThread는 스레드 핸들을 인수로 전달받아 해당 스레드를 강제로 종료한다. 다른 스레드에서 특정 스레드를 강제 종료하고자

 할 때 사용된다. 이 함수는 스레드와 연결된 DLL에게 어떠한 통보도 하지 않으므로 DLL들이 제대로 종료 처리를 하지 못할 수도 있으며,

 할당된 자원들이 제대로 해제되지 않을 수도 있다.

 

 그러므로 스레드를 종료시킬 방법이 없을 경우에만 TerminateThread 함수를 사용해야 하며, 전역 변수나 기타 다른 방법을 통해 스레드에게

 종료 명령을 전달해 스레드가 ExtiThread를 통해 스스로 종료하도록 하는 것이 가장 좋다.

 그러나 ExitThread도 c++ 객체의 소멸자가 호출되지 않고 C 런타임이 만든 고유의 데이터 블록이 해제되지 않는 문제가 발생하기도 한다.

 결국, 스레드가 작업을 무사히 마치고 return 문으로 스레드 시작 함수를 종료하는 것이 가장 바람직하다.

 

<Thread 일시 정지 관련 함수>

 

 스레드의 동작을 잠시 중지시키고 다시 재개시킬 수도 있는데 이때는 하기 함수를 사용한다.

 

 1) Thread 중지 함수.

1
2
3
4
5
DWORD
WINAPI
SuspendThread(
    __in HANDLE hThread
    );
cs

 

 2) Thread 재개 함수.

1
2
3
4
5
DWORD
WINAPI
ResumeThread(
    __in HANDLE hThread
    );
cs

 

 SuspendThread는 스레드의 동작을 중지시키고 ResumeThread는 중지된 스레드를 다시 재개시킨다.

 스레드는 내부적으로 중지 카운트를 유지하는데 이 카운트는 SuspendThread 함수가 호출되면 증가하고 ResumeThread 함수가 호출되면 감소하며

 카운트가 0이 되면 스레드는 재개된다. 그러므로 SuspendThread 함수를 호출한 횟수만큼 ResumeThread 함수를 호출해야지만 스레드가 동작한다.

 

<Thread 중지 / 재개 프로그램>

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
85
86
87
88
89
90
91
struct ThreadParam{
    int x,y,w,h;
    TCHAR* Mes[3];
    int interval;
} Param[3= {
    {101018050"지금 세 개의""배너가 동시에""실행되고 있습니다."100},
    {2101018050"각각 위치와""글자와 주기가""다릅니다."500},
    {4101018050"국민교육현장""국기이 대한 맹세""복무신조."1000},
};
 
 
// 버튼들의 ID
#define ID_R1 101
#define ID_R2 102
#define ID_R3 103
#define ID_PAUSE 104
#define ID_RUN 105
 
DWORD WINAPI ThreadFunc(LPVOID Param)
{
    HDC hdc;
    ThreadParam* p = (ThreadParam*)Param;
    int idx = 0;
    hdc = GetDC(hWndMain);
    for(;;)
    {
        Rectangle(hdc, p->x, p->y, p->+ p->w, p->+ p->h);
        TextOut(hdc, p->x+5, p->y+5, p->Mes[idx%3], strlen(p->Mes[idx%3]));
        GdiFlush();
        Sleep(p->interval);
        idx++;
    }
 
    ReleaseDC(hWndMain, hdc);
    return 0;
}
 
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
    static HANDLE hThread[3];
    static DWORD ThreadID[3];
    static int NowThread = 0;
    int i;
 
    switch(iMessage) 
    {
    case WM_CREATE:
        hWndMain = hWnd;
        CreateWindow("button""중지", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE, 2001008025, hWnd, (HMENU)ID_PAUSE, g_hInst, NULL);
        CreateWindow("button""실행", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE, 2001308025, hWnd, (HMENU)ID_RUN, g_hInst, NULL);
        CreateWindow("button""Thread0", WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON | WS_GROUP, 10010010030, hWnd, (HMENU)ID_R1, g_hInst, NULL);
        CreateWindow("button""Thread1", WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON, 10012010030, hWnd, (HMENU)ID_R2, g_hInst, NULL);
        CreateWindow("button""Thread2", WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON, 10014010030, hWnd, (HMENU)ID_R3, g_hInst, NULL);
        CheckRadioButton(hWnd, ID_R1, ID_R3, ID_R1);
 
        for (i=0; i<3++i) {
            hThread[i] = CreateThread(NULL,0,ThreadFunc, &Param[i], 0&ThreadID[i]);
        }
 
        return 0;
    case WM_COMMAND:
        switch(LOWORD(wParam)) {
        case ID_R1:
            NowThread = 0;
            break;
        case ID_R2:
            NowThread = 1;
            break;
        case ID_R3:
            NowThread = 2;
            break;
        case ID_PAUSE:
            SuspendThread(hThread[NowThread]);
            break;
        case ID_RUN:
            ResumeThread(hThread[NowThread]);
            break;
        }
 
        return 0;
    case WM_DESTROY:
        for (i=0; i<3++i)
            CloseHandle(hThread[i]);
 
        PostQuitMessage(0);
        return 0;
    }
    return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}
 
 
cs

'Programming > Thread' 카테고리의 다른 글

[API] Thread 6 (동기화 - 크리티컬 섹션)  (0) 2017.08.30
[API] Thread 5 (스케줄링)  (0) 2017.08.30
[API] Thread 4 (UI 스레드)  (0) 2017.08.30
[API] Thread 2 (MultiThread)  (0) 2017.08.29
[API] Thread 1 (단일 Thread)  (0) 2017.08.24

+ Recent posts