程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> 匯編語言 >> 事件對象在多線程編程中的應用

事件對象在多線程編程中的應用

編輯:匯編語言

本課中我們將要學習事件對象以及如何在多線程編程中如何使用同步對象。
理論:
上一課中我們演示了如何用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 \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 消息的處理中我們生成事件同步對象並創建線程。我們設置了相關的值讓同步對象生成時處於”無信號”狀態而且在調用了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。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved