Site hosted by Angelfire.com: Build your free website today!

Tutorial 16: Objeto Evento

Aprenderemos qué es un objeto evento y como usarlo en un programa multithilo.

Bajar el ejemplo aquí.

Teoría:

En el tutorial anterior, demostré como se comunican los hilos usando un mensaje de ventana custom. I left out otros dos métodos: variable global y objeto evento. Usaremos los dos en este tutorial.

Un objeto evento es como un conmutador [switch]: tiene dos estados: activado [on] o inactivado [off]. Cuando un objeto es activado [turned on], está en estado "señalado". Cuando es desactivado [turned off], está en estado "no-señalado". Creas un evento y pones en un recorte de código en los hilos releventes para ver el estado del objeto evento. Si el objeto evento está en el estado no señalado, los hilos que esperan serán puestos a dormir [asleep]. Cuando los hilos están en estado de espera, consumen algo de tiempo del CPU.

Creas un objeto evento llamando a la función CreateEvent que tiene la siguiente sintaxis:

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

lpEventAttribute--> Si especificas un valor NULL, el objeto evento es creado con el descriptor de seguridad por defecto.
bManualReset--> Si quieres que Windows automáticamente restablezca el objeto evento a estado no señalado después de llamara WaitForSingleObject, debes especificar FALSE en este parámetro. Sino debes restablecer manualmente el objeto evento con la llamada a ResetEvent.
bInitialState--> Si quieres que el objeto evento sea creado en el estado señalado, especifica TRUE como este parámetro sino el objeto evento será creado en estado no señalado.
lpName --> Puntero a una cadena ASCIIZ que es el nombre de un objeto evento. Este nombre es usado cuando quieres llamar a OpenEvent.

Si la llamada tiene éxito, regresa el manejador al objeto evento creado sino regresa NULL.

Puedes modificar el estado de un objeto evento con dos llamadas a la API: SetEvent y ResetEvent. La función SetEvent pone el objeto evento en estado señalado. ResetEvent lo pone en el estado inverso.
Cuando se crea un objeto, debes poner la llamada a WaitForSingleObject en el hilo que espera por el estado de un objeto evento. WaitForSingleObject tiene la siguiente sintaxis:

WaitForSingleObject proto hObject:DWORD, dwTimeout:DWORD

hObject --> Un manejador a uno de los objetos de sincronización. El objeto evento es uno de los objetos de sincronización.
dwTimeout --> especificar el tiempo en milisegundos que esta función esperará para ser el objeto señalado. Si el tiempo especificado ha pasado y el evento objeto todavía no está en estado señalado, WaitForSingleObject regresa a la instrucción que le llamó. Si quieres esperar por el objeto indefinidamente, debes especificar el valor INFINITE como valor de este parámetro.

Ejemplo:

El ejemplo de abajo despliega una ventana que espera a que el usuario seleccione una orden [command] del menú. Si el usuario selecciona "run thread", el hilo comienza el cálculo salvaje. Cuando finaliza, aparece una caja de mensaje informando al usuario que la tarea está hecha. Durante el tiempo que el hilo está corriendo, el usuario puede seleccionar "stop thread" para detener el hilo.

.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

Análisis:

En este ejemplo, demuestro otra técnica para implementar hilos.

    .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

Puedes ver que creo un objeto evento durante el proceso del mensaje WM_CREATE. Creo el objeto evento en estado no señalado con restableci iento automático. Después de que es creado el objeto evento, creo el hilo. Sin embargo, el hilo no corre de inmediato, porque espera que el objeto evento esté en el estado señalado según el código siguiente:

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

La primera linea del procedimiento de hilo es la llamada a WaitForSingleObject. Espera infinitamente por el estado señalado del objeto evento antes de que retorne. Esto significa que incluso cuando el hilo es creado, lo ponemos en estado durmiente.

Cuando el usuario selecciona la orden "run thread" del menú, ponemos el evento en estado señalado siguiendo este código:

            .if ax==IDM_START_THREAD
                invoke SetEvent,hEventStart

La llamada a SetEvent pone el evento en estado señalado lo cual hace que la llamada a WaitForSingleObject en el procedimiento de hilo regrese y el hilo comience a correr. Cuando el usuario selecciona la orden [command] "stop thread",  ponemos el valor de la variable global "EventStop" en 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

Esto detiene el hilo y salta de nuevo a la llamada a WaitForSingleObject. Nota que no tienes que restablecer manualmente el objeto evento en estado no señalado porque especificamos el parámetro bManualReset de la llamada a CreateEvent como FALSE.


Indice

Siguiente

[Iczelion's Win32 Assembly HomePage]

n u M I T_o r's   Programming Page

Este tutorial, original de Iczelion, ha sido traducido por:   n u M I T_o r