Уроки Iczelion'а

       

Объект события


Мы изучим, что такое объект события и как использовать его в мультитредной программе.

Скачайте пример здесь.

Теория:

В предыдущем туториале я продемонстрировал, как треды взаимодействуют друг с другом через собственные windows-сообщения. Я пропустил два других метода: глобальная переменная и объект события. В этом туториале мы используем оба.

Объект события - это что-то вроде переключателя: у него есть только два состояния: вкл и выкл. Вы создаете объект события и помещаете его в коде соответствующего треда, где наблюдаете за состояние объекта. Если объект события выключен, ждущие его треды "спать". В подобном состоянии треды мало загружают CрU.

Вы можете создать объект события, вызвав функцию CreateEvent, которая имеет следующий синтаксис:

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

  • lpEventAttribute --> Если вы укажете значение NULL, у создаваемого объекта будут установки безопасности по умолчанию.
  • bManualReset --> Если вы хотите, чтобы Windows автоматически переключал объект события в "выключено", вы должны присвоить этому параметру значение FALSE. Иначе вам надо будет выключить объект вручную с помощью вызова ResetEvent.
  • bInitialStae --> Если вы хотите, чтобы объект события при создании был установлен в положение "включено", укажите TRUE в качестве данного параметра, в противном случае объект события будет установлен в положение "выключен".
  • Указатель на ASCIIZ-строку, которая будет именем объекта события. Это имя будет использоваться, когда вы захотите вызвать OpenEvent.

    Если вызов прошел успешно, CreateEvent возвратит хэндл на созданный объект события. В противном случае она возвратит NULL.

    Вы можете изменять состояние объекта события с помощью двух ApI-функций: SetEvent и ResetEvent. Функция SetEvent устанавливает объект события в положение "включено". ResetEvent делает обратное.



    Когда объект события создан, вы должны поместить вызов функции WaitForSingleObject в тред, который должен следить за состоянием объекта события. Эта функция имеет следующий синтаксис:


    WaitForSingleObject proto hObject:DWORD, dwTimeout:DWORD
  • hObject --> Хэндл одного из синхронизационных объектов. Объект события - это вид синхронизационного события.

  • dwTimeout --> Указывает в миллисекундах время, которое эта функция будет ждать, пока объект события не перейдет во включенное состояние. Если указанное время пройдет, а объект события все еще выключен, WaitForSingleObject вернет управление. Если вы хотите, чтобы функция наблюдала за объектом бесконечно, вы должны указать значение INFINITE в качестве этого параметра.

  • Пpимеp:
    Нижеприведенный пример отображает окно, ожидающее пока пользователь не выберет какую-нибудь команду из меню. Если пользователь выберет "run 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
    Анализ:
    В этом примере я демонстрирую другую технику работы с тредами.
    .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. Я создаю объект события, установленного в состояние "выключено" и обладающего свойством автоматического выключения. После того, как объект события создан, я создаю тред. Тем не менее, тред не начинает выполняться немедленно, так как он ждет, пока не включится объект события:
    Threadproc pROC USES ecx param:DWORD
    invoke WaitForSingleObject,hEventStart,INFINITE mov ecx,600000000
    Первая линия процедуры треда - это вызов WainForSingleObject. Она ждет, пока не включится объект события, а затем возвращается. Это означает, что даже если тред создан, мы помещаем его в спящее состояние.
    Когда пользователь выбирает в меню команду "run thread", мы включаем объект события:
    .if ax==IDM_START_THREAD invoke SetEvent,hEventStart
    Вызов SetEvent включает объект события, после чего WainForSingleObject возвращается и тред начинает выполняться. Когда пользователь выбирает команду "stoр thread", мы устанавливаем значение глобальной переменной в 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
    Это останавливает тред и снова передает управление функции WaitForSingleObject. Заметьте, что мы не должны вручную выключать объект, так как мы указали при вызове функции CreateEvent, что значение bManualReset pавно FALSE.
    [C] Iczelion, пер. Aquila.

    Содержание раздела