2007. 2. 16. 13:08
DevX Win32 Assembly Tutorial 16: Event Object

Tutorial 16: Event Object

이 번장에서는, 이벤트 오브젝트가 어떤 것인지를 설명하고, multi-thread 프로그램의 사용 방법을 설명한다.
 소스    리소스 스크립트    실행 결과  

Theory:

이전 장에서 윈도우 메세지를 사용자정의 해서 thread간의 통신방법을 어떻게 처리하는 지를 설명을 했지만, 나머지 2개의 방법(글로벌영역변수를 사용하는 방법과 이벤트 오브젝트를 사용하는 방법)은, 설명하지 않았기 때문에, 이 장에서는 이 2개의 방법을 설명한다.

이벤트 오브젝트는 2가지의 상태만 표현가능하고, 마치 스위치와 같은 구조로 되어 있다. 이벤트 오브젝트 스위치가 ON이 되면, 「Signal」상태로되고, 이벤트 오브젝트 스위치가 OFF가 되면, 「Non Sinal」상태가된다. 이벤트 오브젝트를 생성하면, 해당 이벤트 오브젝트의 상태를 감시할 수 있는 thread에, 감시 프로그램을 작성해야 한다. 만약 이벤트 오브젝트가 Non Signal 상태라면, 이벤트 오브젝트를 기다리는 thread가 대기상태가 된다. thread가 대기 상태일 때, CPU에 부하는 걸리지 않는다.

CreateEvent 함수를 호출하는 것으로, 이벤트 오브젝트를 생성할 수 있다. 이 함수의 프로토타입은다음과 같다.

CreateEvent proto lpEventAttributes : DWORD,\
                  bManualReset      : DWORD,\
                  bInitialState     : DWORD,\
                  lpName            : DWORD

  • lpEventAttribute
    보안속성의 포인터로서 , NULL이면 기본값으로 설정된다
  • bManualReset
    TRUE면 수동 리셋 오브젝트가, FALSE면 자동 리셋 오브젝트가 생성된다. 수동 리셋 오브젝트는 ResetEvent 함수를 호출해서 이벤트 오브젝트를 Non Signal상태로 만들고 , 자동 리셋 오브젝트는 WaitForSingleObject 함수를 호출한 후 자동으로 Non Signal상태가 된다.
  • bInitialState
    이벤트 오브젝트의 초기 상태를 지정한다. TRUE면 Signal상태로 설정하고, FALSE면 Non Signal 상태로 설정한다.
  • lpName
    이벤트 오브젝트명을 지정한다. OpenEvent 함수를 호출 할 경우 이름을 사용하게 된다.

호출이 성공적으로 수행되면 생성된 이벤트 오브젝트의 핸들을 돌려주고, 실패하면, NULL이 설정된다.
이벤트 오브젝트 상태를 변경하는 2개의 API 함수가 있다. SetEvent 함수와 ResetEvent 함수이다. SetEvent 함수는 이벤트 오브젝트를 Signal 상태로 설정하고,ResetEvent 함수는 Non Signal상태로 설정한다.

이벤트 오브젝트가 생성되었을 경우, 이벤트 오브젝트 상태를 감시하는 thread로, WaitForSingleObject 함수를 호출 해야만 한다. WaitForSingleObject 함수의 프로토타입은 다음과 같다.

WaitForSingleObject proto hObject:DWORD, dwTimeout:DWORD

  • hObject
    동기 오브젝트의 핸들. 이벤트 오브젝트는 동기 오브젝트의 일종이다.
  • dwTimeout
    이벤트 오브젝트 상태가 Signal 상태가 될 때까지 대기하는 시간을 밀리세컨드 단위로 지정한다. 만약 지정한 시간내에 Signal상태가 되지 않으면, WaitForSingleObject 함수는 호출한 곳으로 제어를 반환한다. Signal상태가 될 때까지 무한정 대기하고 싶다면, INFINITE를 지정한다.(1000 ms = 1 Second)

Example:

유저가 「run thread」를 선택하면, thread는 오랜 시간이 걸리는 계산코드를 실행한다. 계산 코드가 종료되면, 처리가 종료되었다는 메시지 박스가 표시된다. thread가 실행하는 동안에, 유저가 thread를 중지시킬 수 있도록, 「stop thread」메뉴를 선택할 수 있다.

.386
.model flat, stdcall
option casemap:none
WinMain proto :DWORD, :DWORD, :DWORD, :DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

.const
IDM_START_THREAD equ 1
IDM_STOP_THREAD equ 2
IDM_EXIT equ 3
WM_FINISH equ WM_USER+100h

.data
ClassName db "Win32ASMEventClass", 0
AppName db "Win32 ASM Event Example", 0
MenuName db "FirstMenu", 0
SuccessString db "The calculation is completed! ", 0
StopString db "The thread is stopped", 0
EventStop BOOL FALSE

.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hwnd HANDLE ?
hMenu HANDLE ?
ThreadID DWORD ?
ExitCode DWORD ?
hEventStart HANDLE ?

.code
start:
   invoke GetModuleHandle, NULL
   mov   hInstance, eax
   invoke GetCommandLine
   mov CommandLine, eax
   invoke WinMain, hInstance, NULL, CommandLine, SW_SHOWDEFAULT
   invoke ExitProcess, eax

WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD
   LOCAL wc:WNDCLASSEX
   LOCAL msg:MSG
   mov  wc.cbSize, SIZEOF WNDCLASSEX
   mov  wc.style, CS_HREDRAW or CS_VREDRAW
   mov  wc.lpfnWndProc, OFFSET WndProc
   mov  wc.cbClsExtra, NULL
   mov  wc.cbWndExtra, NULL
   push hInst
   pop  wc.hInstance
   mov  wc.hbrBackground, COLOR_WINDOW+1
   mov  wc.lpszMenuName, OFFSET MenuName
   mov  wc.lpszClassName, OFFSET ClassName
   invoke LoadIcon, NULL, IDI_APPLICATION
   mov  wc.hIcon, eax
   mov  wc.hIconSm, eax
   invoke LoadCursor, NULL, IDC_ARROW
   mov  wc.hCursor, eax
   invoke RegisterClassEx, addr wc
   invoke CreateWindowEx, WS_EX_CLIENTEDGE, ADDR ClassName,\
           ADDR AppName,\
          WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,\
          CW_USEDEFAULT, 300,200, NULL, NULL,\
          hInst, NULL
   mov  hwnd, eax
   invoke ShowWindow, hwnd, SW_SHOWNORMAL
   invoke UpdateWindow, hwnd
   invoke GetMenu, hwnd
   mov hMenu, eax
   .WHILE TRUE
           invoke GetMessage, ADDR msg, NULL, 0,0
           .BREAK .IF (! eax)
           invoke TranslateMessage, ADDR msg
           invoke DispatchMessage, ADDR msg
   .ENDW
   mov    eax, msg.wParam
   ret
WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
   .IF uMsg==WM_CREATE
       invoke CreateEvent, NULL, FALSE, FALSE, NULL
       mov hEventStart, eax
       mov eax, OFFSET ThreadProc
       invoke CreateThread, NULL, NULL, eax,\
                            NULL, 0,\
                            ADDR ThreadID
       invoke CloseHandle, eax
   .ELSEIF uMsg==WM_DESTROY
       invoke PostQuitMessage, NULL
   .ELSEIF uMsg==WM_COMMAND
       mov eax, wParam
       .if lParam==0
           .if ax==IDM_START_THREAD
               invoke SetEvent, hEventStart
               invoke EnableMenuItem, hMenu, IDM_START_THREAD, MF_GRAYED
               invoke EnableMenuItem, hMenu, IDM_STOP_THREAD, MF_ENABLED
           .elseif ax==IDM_STOP_THREAD
               mov EventStop, TRUE
               invoke EnableMenuItem, hMenu, IDM_START_THREAD, MF_ENABLED
               invoke EnableMenuItem, hMenu, IDM_STOP_THREAD, MF_GRAYED
           .else
               invoke DestroyWindow, hWnd
           .endif
       .endif
   .ELSEIF uMsg==WM_FINISH
       invoke MessageBox, NULL, ADDR SuccessString, ADDR AppName, MB_OK
   .ELSE
       invoke DefWindowProc, hWnd, uMsg, wParam, lParam
       ret
.ENDIF
   xor   eax, eax
   ret
WndProc endp

ThreadProc PROC USES ecx Param:DWORD
       invoke WaitForSingleObject, hEventStart, INFINITE
       mov ecx, 600000000
       .WHILE ecx! =0
               .if EventStop! =TRUE
                       add eax, eax
                       dec ecx
               .else
                       invoke MessageBox, hwnd, ADDR StopString, ADDR AppName, MB_OK
                       mov EventStop, FALSE
                       jmp ThreadProc
               .endif
       .ENDW
       invoke PostMessage, hwnd, WM_FINISH, NULL, NULL
       invoke EnableMenuItem, hMenu, IDM_START_THREAD, MF_ENABLED
       invoke EnableMenuItem, hMenu, IDM_STOP_THREAD, MF_GRAYED
       jmp  ThreadProc
       ret
ThreadProc ENDP
end start

Analysis:

이 예제에서는, 이전 장과는 다른 thread 기술을 사용하고 있다.

.IF uMsg==WM_CREATE
    invoke CreateEvent, NULL, FALSE, FALSE, NULL
    mov    hEventStart, eax
    mov    eax, OFFSET ThreadProc
    invoke CreateThread, NULL, NULL, eax, NULL, 0, ADDR ThreadID
    invoke CloseHandle, eax

WM_CREATE 메세지를 받으면, 초기 상태가 Non Signal상태의 자동 리셋형의 이벤트 오브젝트를 생성한 뒤, thread를 생성한다. 그렇지만, thread는 곧바로 작동하지 않는다.이것은 다음의 코드와 같이, 이벤트 오브젝트가 Signal상태가 되도록 기다리기 때문이다.

ThreadProc PROC USES ecx Param:DWORD
       invoke WaitForSingleObject, hEventStart, INFINITE
       mov    ecx, 600000000

thread 함수의 첫행은 WaitForSingleObject 함수의 호출로서, 이벤트 오브젝트가 Signal상태가 될 때까지 제어를 반환하지 않는다. 즉, thread가 생성되었을 때, thread는 정지상태가 되는 것이다.

유저가 「run thread」메뉴를 선택하면, 다음과 같이 이벤트 오브젝트를 Signal상태로 설정한다.

.if ax==IDM_START_THREAD
    invoke SetEvent, hEventStart

SetEvent 함수의 호출로 인해, 이벤트 오브젝트를 Signal상태로 설정하고,이벤트 오브젝트 상태를 감시하는 thread 프로시저를 호출하는 WaitForSingleObject 함수로 제어가 옮겨지고, thread가 시작되게 된다. 유저가 「stop thread」메뉴를 선택하면, 글로벌 변수「EventStop」를 TRUE로 설정한다.

.if EventStop==FALSE
        add    eax, eax
        dec    ecx
.else
        invoke MessageBox, hwnd, ADDR StopString, ADDR AppName, MB_OK
        mov    EventStop, FALSE
        jmp    ThreadProc
.endif

위의 코드에는, thread를 종료하고, WaitForSingleObject 함수를 다시 호출 한 곳으로 점프 한다. 여기서 이벤트 오브젝트를 수동으로 Non Signal상태로 설정 할 필요는 없다. 왜냐면, CreateEvent 함수의 인수중 하나인 bManualReset 를 FALSE로 설정했기 때문이다.


Posted by openserver