事件物件(轉)

heying1229發表於2007-07-28
事件物件:

  本課中我們將要學習事件物件以及如何在多執行緒程式設計中如何使用同步物件。
理論:
上一課中我們演示瞭如何用WINDOWS訊息在不同的執行緒之間進行通訊。另外的兩種,即:使用全域性變數和事件物件,將在本課中講解。
事件物件就像一個開關:它只有兩種狀態---開和關。當一個事件處於”開”狀態,我們稱其為”有訊號”否則稱為”無訊號”。您可以在一個執行緒的執行函式中建立一個事件物件,然後觀察它的狀態,如果是”無訊號”就讓該執行緒睡眠,這樣該執行緒佔用的CPU時間就比較少。
產生事件物件的函式如下:
CreateEvent proto lpEventAttributes:DWORD,
bManualReset:DWORD,
bInitialState:DWORD,
lpName:DWORD

lpEventAttribute--&gt 如果是NULL值,產生的事件物件有預設的屬性。
bManualReset--&gt 如果想在每次呼叫WaitForSingleObject 後讓WINDOWS為您自動地把事件地狀態恢復為”無訊號”狀態,必須把該引數設為FALSE,否則,您必須每次呼叫ResetEvent函式來清除事件的訊號。
bInitialState--&gt 剛剛產生事件物件時的狀態。如果設為TRUE是”有訊號”,否則是”無訊號”。
lpName --&gt 事件物件的名稱。您在OpenEvent函式中可能使用。

如果CreateEvent呼叫成功的話,會返回新生成的物件的控制程式碼,否則返回NULL。
這裡有兩個API函式用來修改事件物件的訊號狀態:SetEvent和ResetEvent。前者把事件物件設為”有訊號”狀態,而後者正好相反。
在事件物件生成後,必須呼叫WaitForSingleObject來讓執行緒進入等待狀態,該函式的語法如下:

WaitForSingleObject proto hObject:DWORD, dwTimeout:DWORD

hObject --&gt指向同步物件的指標。事件物件其實是同步物件的一種。
dwTimeout --&gt 等待同步物件變成”有訊號”前等待的時間,以毫秒計。當等待的時間超過該值後無訊號同步物件仍處於”無訊號”狀態,執行緒不再等待,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。

[@more@]

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10172717/viewspace-928730/,如需轉載,請註明出處,否則將追究法律責任。

相關文章