多线程资料
Windows 环境下32位汇编语言程序设计(第2版)----罗云彬 编著
第12章 多线程
12.2 多线程编程(4)
2. 线程函数
如果创建线程时没有指定CREATE_SUSPENDED标志,当CreateThread函数返回时,lpStartAddress参数指向的线程函数就已经开始运行了。线程函数包含所有需要在线程中执行的代码,它有一个输入参数,线程函数的一般书写格式是:
_ProcThread
proc
uses ebx esi edi lParam
local
局部变量
...
mov
eax,返回码
ret
_ProcThread
endp
读者可以自由定义函数的名称,只要在使用CreateThread函数时将lpStartAddress参数指向函数的入口地址就可以了,lParam参数传递过来的就是调用CreateThread函数时使用的dwParameter参数。
向线程函数传递参数的时候,读者可能会觉得一个lParam参数不太够用,如果需要传递多个参数该怎么办呢?其实这不是问题,因为子线程和主线程使用同一个地址空间,主线程可以通过全局变量来传递参数。
有时候也可能遇到这种情况:进程中存在多个子线程,这些子线程的线程函数使用同一个子程序,如果对这些子线程使用同样的全局变量传递参数,难免会引起冲突。这时可以为每个子线程分配一个存放参数的内存块,主线程通过lParam参数把内存块的指针传递给子线程,子线程通过这个指针存取内存块中的内容就可以了,不过在子线程结束的时候不要忘了释放内存块。
3. 终止线程
线程从线程函数的第一句代码开始执行,直到线程被终止为止。当线程被正常终止时,系统会进行下面的操作:
●
线程使用的堆栈被释放。
●
系统将线程对象中的退出代码设置为线程的退出码。
●
系统将递减线程对象的使用计数。
线程结束后的退出码可以被其他线程用GetExitCodeThread函数检测到,所以可以当做自定义的返回值来表示线程执行的结果。终止一个线程的执行有4种方法。
第1种方法是线程函数的自然退出,当函数执行到一句ret指令返回时,Windows将终止线程的执行,这时放在eax中的返回值就是线程的退出码。一般建议使用这种方法终止一个线程的执行。
第2种方法是使用ExitThread函数来终止线程:
invoke
ExitThread,dwExitCode
ExitThread函数只能用于终止当前线程,它并不能用于在一个线程中终止另外一个线程,和ExitProcess函数一样,ExitThread函数不会有返回的时候。dwExitCode参数指定为线程的退出码。使用ExitThread函数和使用ret指令终止线程的效果是一样的,但显然不如使用ret指令来得简洁和方便。
第3种方法是使用TerminateThread函数,这个函数可以用来在一个线程中强制终止另一个线程的执行:
invoke
TerminateThread,hThread,dwExitCode
hThread参数指定需要终止的线程句柄,dwExitCode将用做被终止线程的退出码。如果函数执行成功,返回值是非0值,否则函数返回0,但是TerminateThread函数是一个异步执行的函数,即使函数返回非0值,也并不代表目标线程已经终止,可能终止的过程还要延续一段时间,如果必须确认线程已经真正结束的话,可以使用GetExitCodeThread函数来检测。
TerminateThread函数是一个被强烈建议避免使用的函数,因为一旦执行这个函数,程序无法预测目标线程会在何处被终止,其结果就是目标线程可能根本没有机会来做清除工作。读者可以尝试在Counter.asm例子中使用TerminateThread函数来终止_Counter线程的执行。可以发现计数线程是停止了,但是“停止计数”按钮并不会恢复为“计数”按钮,“暂停/恢复”按钮也不会被灰化。因为计数线程平时在循环中执行,被强制终止的时候必然还在循环体内,这样下面的扫尾代码将没有机会执行,其结果当然如此了:
invoke
SetWindowText,hWinCount,addr szStart
invoke
EnableWindow,hWinPause,FALSE
and
dwOption,not (F_COUNTING or F_STOP or F_PAUSE)
TerminateThread函数引发的问题可能还有很多,如线程中打开的文件和申请的内存等都不会被释放,更危险的是,如果线程刚好在调用Kernel32.dll中的系统函数时被终止,可能会引起Kernel32的状态处于不正确的状态(当然只是线程所属进程的Kernel32状态而不是系统范围的状态)。另外,当使用TerminateThread函数终止线程的时候,系统不会释放线程使用的堆栈。所以建议读者在编程中的时候尽量让线程自己退出,如果主线程要求某个线程结束,可以通过各种方法通知线程,线程收到通知在做扫尾工作后自行退出。只有在迫不得已的情况下,才能使用TerminateThread函数去终止一个线程。
第4种方法就是使用ExitProcess函数结束进程,这时系统会自动结束进程中所有线程的运行。在以前演示的所有的单线程程序中,并不显式地结束主线程的运行,而总是用直接结束进程的方法让主线程自然结束。在多线程的程序中,用这种方法结束线程相当于对每个线程使用TerminateThread函数,所以也应当避免这种情况(用这种方法结束主线程的运行并不是问题,因为在这之前可以预测到线程的结束并进行扫尾工作)。
当一个线程终止时,Windows释放执行线程所需的各种资源,如堆栈与寄存器环境等,并且不再继续分配时间片调用线程中的代码,但线程对象并不马上被释放,因为以后其他线程可能还需要用GetExitCodeThread函数检测线程的退出码。线程对象一直保存到使用CloseHandle函数关闭线程句柄为止。