ITPub博客

首页 > IT职业 > IT生活 > iczelion tut16 (转)

iczelion tut16 (转)

原创 IT生活 作者:worldblog 时间:2007-12-09 09:43:42 0 删除 编辑
iczelion tut16 (转)[@more@]

第十六课 事件对象


本课中我们将要学习事件对象以及如何在多线程编程中如何使用同步对象。

理论:

上一课中我们演示了如何用windows消息在不同的线程之间进行通讯。另外的两种,即:使用全局变量和事件对象,将在本课中讲解。
事件对象就像一个开关:它只有两种状态---开和关。当一个事件处于”开”状态,我们称其为”有信号”否则称为”无信号”。您可以在一个线程的执行函数中创建一个事件对象,然后观察它的状态,如果是”无信号”就让该线程睡眠,这样该线程占用的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函数会返回。如果想要线程一直等待,请把该参数设为INFINITE(该值等于0xffffffff)。

例子:

下面的例子显示了一个窗口,当用户选择了菜单项”run thread”后,线程开始简单的计数运算。结束后弹出一个对话框通知用户。在整个的计数期间,您可以选择菜单项”stop thread”来随时终止线程。

.386
.model flat,stdcall
option casemap:none
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include masm32includewindows.inc
include masm32includeuser32.inc
include masm32includekernel32.inc
includelib masm32libuser32.lib
includelib masm32libkernel32.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 消息的处理中我们生成事件同步对象并创建线程。我们设置了相关的值让同步对象生成时处于”无信号”状态而且在调用了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,MB_OK
  mov  EventStop,FALSE
  jmp ThreadProc
  .endif

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



来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/10752043/viewspace-990291/,如需转载,请注明出处,否则将追究法律责任。

上一篇: iczelion tut17 (转)
下一篇: iczelion tut18 (转)
请登录后发表评论 登录
全部评论
  • 博文量
    6241
  • 访问量
    2411234