⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 016.txt

📁 会变语言实现的一些程序
💻 TXT
字号:
第十六课 事件对象


--------------------------------------------------------------------------------
本课中我们将要学习事件对象以及如何在多线程编程中如何使用同步对象。 
理论:
上一课中我们演示了如何用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。 


--------------------------------------------------------------------------------




--------------------------------------------------------------------------------

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -