Programming 版 (精华区)

发信人: zhangyan (今朝有水今朝灌), 信区: Programming
标  题: Iczelion Win32汇编教程(16)
发信站: 哈工大紫丁香 (2001年02月14日16:22:40 星期三), 站内信件

第十六课 事件对象
----------------------------------------------------------------------------
----
本课中我们将要学习事件对象以及如何在多线程编程中如何使用同步对象。
理论:
上一课中我们演示了如何用WINDOWS消息在不同的线程之间进行通讯。另外的两种,即:
使用全局变量和事件对象,将在本课中讲解。
事件对象就像一个开关:它只有两种状态---开和关。当一个事件处于”开”状态,我们
称其为”有信号”否则称为”无信号”。您可以在一个线程的执行函数中创建一个事件
对象,然后观察它的状态,如果是”无信号”就让该线程睡眠,这样该线程占用的CPU时
间就比较少。
产生事件对象的函数如下:
CreateEvent proto lpEventAttributes:DWORD,\
                             bManualReset:DWORD,\
                             bInitialState:DWORD,\
                             lpName:DWORD
lpEventAttribute--> 如果是NULL值,产生的事件对象有缺省的安全属性。
bManualReset--> 如果想在每次调用WaitForSingleObject 后让WINDOWS为您自动地把事
件地状态恢复为”无信号”状态,必须把该参数设为FALSE,否则,您必须每次调用Rese
tEvent函数来清除事件的信号。
bInitialState--> 刚刚产生事件对象时的状态。如果设为TRUE是”有信号”,否则是”
无信号”。
lpName --> 事件对象的名称。您在OpenEvent函数中可能使用。
如果CreateEvent调用成功的话,会返回新生成的对象的句柄,否则返回NULL。
这里有两个API函数用来修改事件对象的信号状态:SetEvent和ResetEvent。前者把事件
对象设为”有信号”状态,而后者正好相反。
在事件对象生成后,必须调用WaitForSingleObject来让线程进入等待状态,该函数的语
法如下:
WaitForSingleObject proto hObject:DWORD, dwTimeout:DWORD
hObject -->指向同步对象的指针。事件对象其实是同步对象的一种。
dwTimeout --> 等待同步对象变成”有信号”前等待的时间,以毫秒计。当等待的时间
超过该值后无信号同步对象仍处于”无信号”状态,线程不再等待,WaitForSingleObj
ect函数会返回。如果想要线程一直等待,请把该参数设为INFINITE(该值等于0xfffff
fff)。
例子:
下面的例子显示了一个窗口,当用户选择了菜单项”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,M
B_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,INFINITE
       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,M
B_OK
                       mov  EventStop,FALSE
                       jmp ThreadProc
               .endif
这样线程得计数工作结束,然后跳转到重新执行WaitForSingleObject函数的地方。注意
:我们不用手动清除事件对象的信号,因为在调用CreateEvent函数时把参数bManualRe
set的值设为了FALSE。
----------------------------------------------------------------------------
----
--
Take it slow,     Set it couse,     Make it happen.

※ 来源:·哈工大紫丁香 bbs.hit.edu.cn·[FROM: 202.97.235.21]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:7.273毫秒