当前位置:中国易下载软件教材中心文章中心编程语言汇编语言 → 汇编语言教学(15)

汇编语言教学(15)

减小字体 增大字体 作者:佚名  来源:不详  发布时间:2007-8-21 1:04:01
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
喜欢这些内容嘛,请告诉你身边的朋友,易下载中心-QQ资源-itnetcn.com一起享受这份乐趣,本站内容来源互联网
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

第十五课 多线程编程8LB海岸线网络安全资讯站
本课中,我们将学习如何进行多线程编程。另外我们还将学习如何在不同的线程间进行通信。 8LB海岸线网络安全资讯站
理论:8LB海岸线网络安全资讯站
前一课中,我们学习了进程,其中讲到每一个进程至少要有一个主线程。这个线程其实是进程执行的一条线索,除此主线程外您还可以给进程增加其它的线程,也即增加其它的执行线索,由此在某种程度上可以看成是给一个应用程序增加了多任务功能。当程序运行后,您可以根据各种条件挂起或运行这些线程,尤其在多CPU的环境中,这些线程是并发运行的。这些是在W32下才有的概念,在WIN16下并没有等同的概念。8LB海岸线网络安全资讯站
在同一进程中运行不同的线程的好处是这些线程可以共享进程的资源,如全局变量、资源等。当然各个线程也可以有自己的私有栈用于保存私有数据。另外每个线程需要保存其运行上下文以便在线程切换时能够记住或恢复其上下文,当然这是由操作系统来完成的,对于用户是透明的。8LB海岸线网络安全资讯站
我们大体上可以把线程分成两大类: 8LB海岸线网络安全资讯站
处理用户界面的线程:该类线程产生自己的窗口并负责处理相关的窗口消息。用户界面线程遵守WIN16下的互斥原则,即没一刻仅有一个用户界面线程使用USER和GDI库中的内核函数,也就是说当一个用户界面程序在进入GDI或USER中时,内核不允许重入。由此我们可以推论出WIN95的该部分内核的代码是遵守16位模式的。而WINOWS NT是纯的32位操作系统,所以不存在这个问题。 8LB海岸线网络安全资讯站
工作者线程:该类线程不用处理窗口界面,当然也就不用处理消息了。它一般都运行在后台干一些计算之类的粗,这大概也是把它叫做工作者线程的原因吧。 8LB海岸线网络安全资讯站
运用W32的多线程模式来编程,我们可以遵循某种策略:即让主线程仅来做用户界面的工作,而其它繁重的工作则交由工作者线程在后台完成。这就好比我们日常生活中的许多例子。譬如:政府管理者好比是用户界面线程,它负责听取民意,给职能部门分配工作,然后把工作成果汇报给公众。而具体的职能部门就是工作者线程,它负责完成下达的具体工作。如果让政府管理这来具体地做每一件事,它必须作一件事后再做另一项,那它就不能及时来听取和反馈民意。这样就无法管理好一个国家了。当然即使采用多线程制,政府管理部门也不一定就能管理好国家,但是程序却可以采用多线程机制来管理好她自己的工作。我们可以调用CreateThread函数来生成新线程。该函数的语法如下:8LB海岸线网络安全资讯站
8LB海岸线网络安全资讯站
CreateThread proto lpThreadAttributes:DWORD,\ 8LB海岸线网络安全资讯站
dwStackSize:DWORD,\ 8LB海岸线网络安全资讯站
lpStartAddress:DWORD,\ 8LB海岸线网络安全资讯站
lpParameter:DWORD,\ 8LB海岸线网络安全资讯站
dwCreationFlags:DWORD,\ 8LB海岸线网络安全资讯站
lpThreadId:DWORD 8LB海岸线网络安全资讯站
8LB海岸线网络安全资讯站
生成一个线程的函数和生成一个进程基本相同。8LB海岸线网络安全资讯站
lpThreadAttributes -->如果您想要线程有缺省的安全属性,可以置该值为NULL。 8LB海岸线网络安全资讯站
dwStackSize --> 指定线程的堆栈大小。如果为0,那线程的大小和进程相同。 8LB海岸线网络安全资讯站
lpStartAddress--> 线程函数的起始地址。注意该函数仅接收一个32位的参数和返回一个32位的值。(该参数可以是一个指针,而且进程的线程可以直接存取进程定义全局变量,所以您大可不必担心不能如何把大量的参数传递给线程)。 8LB海岸线网络安全资讯站
lpParameter --> 传递给线程的上下文。 8LB海岸线网络安全资讯站
dwCreationFlags -->如果是0的话则表示创线程建后立即启动,相反的是标志位CREATE_SUSPENDED,这样您需要稍后显示地让该线程运行。8LB海岸线网络安全资讯站
lpThreadId --> 内核给新生成的线程分配的线程ID。 8LB海岸线网络安全资讯站
8LB海岸线网络安全资讯站
如果生成线程成功的话,CreateThread函数就返回新线程的句柄。否则返回NULL。8LB海岸线网络安全资讯站
如果没有给参数dwCreationFlags指定CREATE_SUSPENDED的话,该线程就会立即运行。如果不这样,我们上面说了,需要显示地启动该线程,要这样做您需要调用ResumeThread函数。8LB海岸线网络安全资讯站
在线程返回后(线程的执行类似与执行一个函数,如果它调用了最后一条指令后,在汇编中是ret,那么该线程就结束了,除非您让它进入一个循环,譬如我们讲的用户界面线程就是如此,只不过它不退出的原因是进入的循环是在{while ( GetMessage(...))...}中,如果您没有给它传递一个值为0的消息,那它可不会退出),系统会自动调用ExitThread函数透明地处理线程一些退出时的清理工作。当然您可以自己调用该函数,但似乎没有什么意义。要得到退出时的退出码,您可以调用GetExitCodeThread函数。 8LB海岸线网络安全资讯站
如果您想结束一个程序,可以调用TerminateThread函数,不过使用该函数要小心行事,因为该函数一旦被调用线程就会退出,这样它就没有机会来做清理自己的工作了。 8LB海岸线网络安全资讯站
8LB海岸线网络安全资讯站
现在我们来看看线程间的通讯机制。 8LB海岸线网络安全资讯站
总的说来一共有三种方法: 8LB海岸线网络安全资讯站
8LB海岸线网络安全资讯站
使用全局变量 8LB海岸线网络安全资讯站
使用Windows消息传递机制 8LB海岸线网络安全资讯站
使用事件 8LB海岸线网络安全资讯站
上面我们说了线程会共享进程的资源,其中全局变量也包括在内,所以线程可以通过使用全局变量来通讯。但是这种办法的明显的缺点是在有多个线程存取同一个全局变量时,必须考虑同步的问题。譬如:有一个有十个成员变量的结构体,其中一个线程在对起赋值时,假设只更新了五个成员变量的值,这时内核的调度线程剥夺其运行权给另一个线程,这样接下来的线程如果想要用该全局结构体变量,它的值就显然不对了。另外多线程的程序也很难调试,尤其这些错误很隐蔽和很难复现时。如果两个线程都是用户界面线程时,用WINDOWS的消息机制来进行线程间的通讯是比较方便的. 8LB海岸线网络安全资讯站
您所要做的只是自定义一些windows消息(注意不要和windows的预定义的消息冲突),然后在线程之间传递可以了。您可以这样来定义消息,把WM_USER(它的值等于0x0400)当作基数,然后顺序地去加序号,譬如: 8LB海岸线网络安全资讯站
WM_MYCUSTOMMSG equ WM_USER+100h 8LB海岸线网络安全资讯站
8LB海岸线网络安全资讯站
小于WM_USER 的值是Windows系统的保留值,大于该值留给用户来使用。8LB海岸线网络安全资讯站
如果其中有一个线程是工作者线程的话,那就不能用该种方法来进行通讯了,这是因为工作者线程没有消息队列。您应当用下面这种策略来进行工作者线程和用户界面线程之间的通讯: 8LB海岸线网络安全资讯站
8LB海岸线网络安全资讯站
User interface Thread ------> global variable(s)----> Worker thread 8LB海岸线网络安全资讯站
Worker Thread ------> custom window message(s) ----> User interface Thread 8LB海岸线网络安全资讯站
8LB海岸线网络安全资讯站
稍后我们的例子中将讲解这种通讯办法。8LB海岸线网络安全资讯站
最后的办法是事件对象。您可以把事件对象看作是一种标志。如果事件对象的状态是无信号的话,说明该线程正在睡眠或挂起,在该种状态下系统是不会给该线程分配CPU时间片的。当一个线程的状态转成有信号时,WINDOWS就会唤醒该线程并且让它正常运行。 8LB海岸线网络安全资讯站
8LB海岸线网络安全资讯站
例子:8LB海岸线网络安全资讯站
您可以下载例子并运行thread1.exe,然后激活菜单项"Savage Calculation",然后程序开始执行指令"add eax,eax ",一共执行600,000,000次,您会发现在这个过程当中,用户界面将停止响应,您既不能使用菜单,也不能使用移动窗口。等到计算完成后,会弹出一个对话框,关闭掉对话框后窗口才可以和当初一样正常运行了。8LB海岸线网络安全资讯站
为了避免这种不便,我们把计算的工作放入到一个单独的工作者线程中去,而主窗口仅仅响应用户的活动。您可以看到虽然用户界面的反应比平常时慢了,但还是可以工作的。 8LB海岸线网络安全资讯站
.386 8LB海岸线网络安全资讯站
.model flat,stdcall 8LB海岸线网络安全资讯站
option casemap:none 8LB海岸线网络安全资讯站
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD 8LB海岸线网络安全资讯站
include \masm32\include\windows.inc 8LB海岸线网络安全资讯站
include \masm32\include\user32.inc 8LB海岸线网络安全资讯站
include \masm32\include\kernel32.inc 8LB海岸线网络安全资讯站
includelib \masm32\lib\user32.lib 8LB海岸线网络安全资讯站
includelib \masm32\lib\kernel32.lib 8LB海岸线网络安全资讯站
8LB海岸线网络安全资讯站
.const 8LB海岸线网络安全资讯站
IDM_CREATE_THREAD equ 1 8LB海岸线网络安全资讯站
IDM_EXIT equ 2 8LB海岸线网络安全资讯站
WM_FINISH equ WM_USER+100h 8LB海岸线网络安全资讯站
8LB海岸线网络安全资讯站
.data 8LB海岸线网络安全资讯站
ClassName db "Win32ASMThreadClass",0 8LB海岸线网络安全资讯站
AppName db "Win32 ASM MultiThreading Example",0 8LB海岸线网络安全资讯站
MenuName db "FirstMenu",0 8LB海岸线网络安全资讯站
SuccessString db "The calculation is completed!",0 8LB海岸线网络安全资讯站
8LB海岸线网络安全资讯站
.data? 8LB海岸线网络安全资讯站
hInstance HINSTANCE ? 8LB海岸线网络安全资讯站
CommandLine LPSTR ? 8LB海岸线网络安全资讯站
hwnd HANDLE ? 8LB海岸线网络安全资讯站
ThreadID DWORD ? 8LB海岸线网络安全资讯站
8LB海岸线网络安全资讯站
.code 8LB海岸线网络安全资讯站
start: 8LB海岸线网络安全资讯站
invoke GetModuleHandle, NULL 8LB海岸线网络安全资讯站
mov hInstance,eax 8LB海岸线网络安全资讯站
invoke GetCommandLine 8LB海岸线网络安全资讯站
mov CommandLine,eax 8LB海岸线网络安全资讯站
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT 8LB海岸线网络安全资讯站
invoke ExitProcess,eax 8LB海岸线网络安全资讯站
8LB海岸线网络安全资讯站
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD 8LB海岸线网络安全资讯站
LOCAL wc:WNDCLASSEX 8LB海岸线网络安全资讯站
LOCAL msg:MSG 8LB海岸线网络安全资讯站
mov wc.cbSize,SIZEOF WNDCLASSEX 8LB海岸线网络安全资讯站
mov wc.style, CS_HREDRAW or CS_VREDRAW 8LB海岸线网络安全资讯站
mov wc.lpfnWndProc, OFFSET WndProc 8LB海岸线网络安全资讯站
mov wc.cbClsExtra,NULL 8LB海岸线网络安全资讯站
mov wc.cbWndExtra,NULL 8LB海岸线网络安全资讯站
push hInst 8LB海岸线网络安全资讯站
pop wc.hInstance 8LB海岸线网络安全资讯站
mov wc.hbrBackground,COLOR_WINDOW+1 8LB海岸线网络安全资讯站
mov wc.lpszMenuName,OFFSET MenuName 8LB海岸线网络安全资讯站
mov wc.lpszClassName,OFFSET ClassName 8LB海岸线网络安全资讯站
invoke LoadIcon,NULL,IDI_APPLICATION 8LB海岸线网络安全资讯站
mov wc.hIcon,eax 8LB海岸线网络安全资讯站
mov wc.hIconSm,eax 8LB海岸线网络安全资讯站
invoke LoadCursor,NULL,IDC_ARROW 8LB海岸线网络安全资讯站
mov wc.hCursor,eax 8LB海岸线网络安全资讯站
invoke RegisterClassEx, addr wc 8LB海岸线网络安全资讯站
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,\ 8LB海岸线网络安全资讯站
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\ 8LB海岸线网络安全资讯站
CW_USEDEFAULT,300,200,NULL,NULL,\ 8LB海岸线网络安全资讯站
hInst,NULL 8LB海岸线网络安全资讯站
mov hwnd,eax 8LB海岸线网络安全资讯站
invoke ShowWindow, hwnd,SW_SHOWNORMAL 8LB海岸线网络安全资讯站
invoke UpdateWindow, hwnd 8LB海岸线网络安全资讯站
.WHILE TRUE 8LB海岸线网络安全资讯站
invoke GetMessage, ADDR msg,NULL,0,0 8LB海岸线网络安全资讯站
.BREAK .IF (!eax) 8LB海岸线网络安全资讯站
invoke TranslateMessage, ADDR msg 8LB海岸线网络安全资讯站
invoke DispatchMessage, ADDR msg 8LB海岸线网络安全资讯站
.ENDW 8LB海岸线网络安全资讯站
mov eax,msg.wParam 8LB海岸线网络安全资讯站
ret 8LB海岸线网络安全资讯站
WinMain endp 8LB海岸线网络安全资讯站
8LB海岸线网络安全资讯站
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM 8LB海岸线网络安全资讯站
.IF uMsg==WM_DESTROY 8LB海岸线网络安全资讯站
invoke PostQuitMessage,NULL 8LB海岸线网络安全资讯站
.ELSEIF uMsg==WM_COMMAND 8LB海岸线网络安全资讯站
mov eax,wParam 8LB海岸线网络安全资讯站
.if lParam==0 8LB海岸线网络安全资讯站
.if ax==IDM_CREATE_THREAD 8LB海岸线网络安全资讯站
mov eax,OFFSET ThreadProc 8LB海岸线网络安全资讯站
invoke CreateThread,NULL,NULL,eax,\ 8LB海岸线网络安全资讯站
0,\ 8LB海岸线网络安全资讯站
ADDR ThreadID 8LB海岸线网络安全资讯站
invoke CloseHandle,eax 8LB海岸线网络安全资讯站
.else 8LB海岸线网络安全资讯站
invoke DestroyWindow,hWnd 8LB海岸线网络安全资讯站
.endif 8LB海岸线网络安全资讯站
.endif 8LB海岸线网络安全资讯站
.ELSEIF uMsg==WM_FINISH 8LB海岸线网络安全资讯站
invoke MessageBox,NULL,ADDR SuccessString,ADDR AppName,MB_OK 8LB海岸线网络安全资讯站
.ELSE 8LB海岸线网络安全资讯站
invoke DefWindowProc,hWnd,uMsg,wParam,lParam 8LB海岸线网络安全资讯站
ret 8LB海岸线网络安全资讯站
.ENDIF 8LB海岸线网络安全资讯站
xor eax,eax 8LB海岸线网络安全资讯站
ret 8LB海岸线网络安全资讯站
WndProc endp 8LB海岸线网络安全资讯站
8LB海岸线网络安全资讯站
ThreadProc PROC USES ecx Param:DWORD 8LB海岸线网络安全资讯站
mov ecx,600000000 8LB海岸线网络安全资讯站
Loop1: 8LB海岸线网络安全资讯站
add eax,eax 8LB海岸线网络安全资讯站
dec ecx 8LB海岸线网络安全资讯站
jz Get_out 8LB海岸线网络安全资讯站
jmp Loop1 8LB海岸线网络安全资讯站
Get_out: 8LB海岸线网络安全资讯站
invoke PostMessage,hwnd,WM_FINISH,NULL,NULL 8LB海岸线网络安全资讯站
ret 8LB海岸线网络安全资讯站
ThreadProc ENDP 8LB海岸线网络安全资讯站
8LB海岸线网络安全资讯站
end start 8LB海岸线网络安全资讯站
8LB海岸线网络安全资讯站
8LB海岸线网络安全资讯站
分析:8LB海岸线网络安全资讯站
主程序的主线程是一个用户界面线程,它有一个普通窗口。用户选择菜单项"Create Thread",程序就会产生一个线程: 8LB海岸线网络安全资讯站
.if ax==IDM_CREATE_THREAD 8LB海岸线网络安全资讯站
mov eax,OFFSET ThreadProc 8LB海岸线网络安全资讯站
invoke CreateThread,NULL,NULL,eax,\ 8LB海岸线网络安全资讯站
NULL,0,\ 8LB海岸线网络安全资讯站
ADDR ThreadID 8LB海岸线网络安全资讯站
invoke CloseHandle,eax 8LB海岸线网络安全资讯站
8LB海岸线网络安全资讯站
上面的代码段产生一个线程,线程的主体代码是函数ThreadProc,该函数和主线程并行运行。在调用成功后,CreateThread函数立即返回,ThreadProc也开始运行。因为我们不再用线程句柄,我们立即关闭它以避免内存泄漏。我们前面讲过关闭句柄不会终止线程的执行,而只是减少起引用计数。 8LB海岸线网络安全资讯站
8LB海岸线网络安全资讯站
ThreadProc PROC USES ecx Param:DWORD 8LB海岸线网络安全资讯站
mov ecx,600000000 8LB海岸线网络安全资讯站
Loop1: 8LB海岸线网络安全资讯站
add eax,eax 8LB海岸线网络安全资讯站
dec ecx 8LB海岸线网络安全资讯站
jz Get_out 8LB海岸线网络安全资讯站
jmp Loop1 8LB海岸线网络安全资讯站
Get_out: 8LB海岸线网络安全资讯站
invoke PostMessage,hwnd,WM_FINISH,NULL,NULL 8LB海岸线网络安全资讯站
ret 8LB海岸线网络安全资讯站
ThreadProc ENDP 8LB海岸线网络安全资讯站
8LB海岸线网络安全资讯站
我们看到上面的线程的代码仅仅是做简单的计数工作,因为我们设了一个很大的基数,所以该线程会持续一段您能感觉得到的时间,当结束后它会向主线程发送WM_FINISH消息。WM_FINISH消息是我们自己定义的,它的定义如下: 8LB海岸线网络安全资讯站
8LB海岸线网络安全资讯站
WM_FINISH equ WM_USER+100h 8LB海岸线网络安全资讯站
WM_USER消息是我们能够使用的最小消息值。8LB海岸线网络安全资讯站
显然我们一看到WM_FINISH,就能从字面上理解该消息的意义。主线程接收到该消息后,会弹出一个对话框告诉用户,计算线程已经结束了。8LB海岸线网络安全资讯站
通过线程之间的通讯,用户可以多次选择"Create Thread",那样就可以运行多个计算线程了。8LB海岸线网络安全资讯站
本例子中,线程之间的通讯是单向的。如果您想让主线程也能向工作者线程发送消息的话,譬如加入一个菜单项来控制工作者线程的结束,您可以这样做: 8LB海岸线网络安全资讯站
add a menu item saying something like "Kill Thread" in the menu 8LB海岸线网络安全资讯站
a global variable which is used as a command flag. TRUE=Stop the thread, FALSE=continue the thread 8LB海岸线网络安全资讯站
Modify ThreadProc to check the value of the command flag in the loop. 8LB海岸线网络安全资讯站
设立一个全局变量,当线程启动前,我们设置它的值为FALSE,当用户激活了我们加的菜单项时,该值变成TRUE。在线程的代码段ThreadProc中每次减1前,判断该值,如果为TRUE的话线程就结束循环体中的计算并退出线程。8LB海岸线网络安全资讯站