Event事件对象

事件对象就像一个开关:它只有两种状态---开和关。当一个事件处于”开”状态,我们称其为”有信号”否则称为”无信号”。您可以在一个线程的执行函数中创建一个事件对象,然后观察它的状态,如果是”无信号”就让该线程睡眠,这样该线程占用的CPU时间就比较少。 
产生事件对象的函数如下:

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

lpEventAttribute--> 如果是NULL值,产生的事件对象有缺省的安全属性。
bManualReset--> 如果想在每次调用WaitForSingleObject 后让WINDOWS为您自动地把事件地状态恢复为”无信号”状态,必须把该参数设为FALSE,否则,您必须每次调用ResetEvent函数来清除事件的信号。
bInitialState--> 刚刚产生事件对象时的状态。如果设为TRUE是”有信号”,否则是”无信号”。
lpName --> 事件对象的名称。您在OpenEvent函数中可能使用。

如果CreateEvent调用成功的话,会返回新生成的对象的句柄,否则返回NULL。
这里有两个API函数用来修改事件对象的信号状态:SetEvent和ResetEvent。前者把事件对象设为”有信号”状态,而后者正好相反。
在事件对象生成后,必须调用WaitForSingleObject来让线程进入等待状态,该函数的语法如下:

WaitForSingleObject proto hObject:DWORD, dwTimeout:DWORD

hObject -->指向同步对象的指针。事件对象其实是同步对象的一种。
dwTimeout --> 等待同步对象变成”有信号”前等待的时间,以毫秒计。当等待的时间超过该值后无信号同步对象仍处于”无信号”状态,线程不再等待,WaitForSingleObject函数会返回。如果想要线程一直等待,请把该参数设为INFIN99vE(该值等于0xffffffff)。

例子:
下面的例子显示了一个窗口,当用户选择了菜单项”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_EX99v 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,INFIN99vE 
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 消息的处理中我们生成事件同步对象并创建线程。我们设置了相关的值让同步对象生成时处于”无信号”状态而且在调用了WaitForSingleObject后可以自动把事件对象的状态设为”无信号”。然后我们创建线程。 线程的代码开始执行后立即被阻塞:

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

您可以看到线程的执行体的第一条代码就是调用WaitForSingleObject函数,该函数使得线程阻塞并且一直处于等待事件对象变成”有信号”。这也就是说,我们以开始就让该线程进入了睡眠状态。 当用户选择了菜单项”run thread”后,我们把事件对象得状态变成”有信号”:

.if ax==IDM_START_THREAD 
invoke SetEvent,hEventStart

函数SetEvent可以让同步对象变成”有信号”状态,那么下一次线程得到时间片运行时,WaitForSingleObject函数就会返回,线程余下的代码就可以得到执行了。当用户选择了菜单项”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

这样线程得计数工作结束,然后跳转到重新执行WaitForSingleObject函数的地方。注意:我们不用手动清除事件对象的信号,因为在调用CreateEvent函数时把参数bManualReset的值设为了FALSE。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值