线程的第一次接触
1. 产生一个线程
HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,DWORD dwStackSize,LPTHREAD-START_ROUTINE lpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadId);
参数:lpThreadAttributes:描述施行于这一新线程的security属性。NULL表示使用缺省值。此参数在Windows95中被忽略。
dwStackSize:新线程拥有自己的堆栈,0表示使用缺省大小:1MB。
lpStactAddress:新线程将开始的起始地址,这是一个函数指针。(c语言中函数名称即代表函数指针,所以这里可放一个函数名称)
lpParameter:此值将被传送到上述所指定之心线程函数去,作为一个参数。
dwCreationFlags:允许你产生一个暂时挂起的线程,默认情况是“立即开始执行”。
lpThread:新线程的ID会被传回这里。
函数调用失败,可以调用GetLastError()获得原因。
2. 多线程程序的特点
(1) 多线程程序是无法预测其行为的。
(2) 线程的执行顺序无法保证。Task Switches可能在任何时刻任何地点发生,线程对于小的该百年有高度的敏感,线程并不总是马上启动。
3. 核心对象(Kernel Objects)
核心对象其实和所谓的GDI对象,如画笔、画刷或DC是差不多的,只不过它有KERNEL32.DLL管理,而非GD132.DLL管理,两种对象之间有许多相似性。
GDI对象是Windows的基础部分,它们都是由操作系统管理,通常你不需要知道其数据格式,WINDOWS隐藏了实现细节,只是给你一个HDC或一个HBRUSH,那都是对象的handle。
核心对象HANDLE为使用时的参考依据。与GDI的HBRUSH、HPEN以及其他种handles不同的是,只有一种handle可以代表核心对象。所谓handle,其实是个指针,指向操作系统内存空间中的某样东西,那东西不允许你直接取得。你的程序不能够直接取用它,为的是维护系统的完整性和安全性。
下面是各种Win32核心对象的清单:进程(process)、线程(threads)、文件(files)、事件(events)、信号量(semaphores)、互斥器(mutexes)、管道(pipes,分为named和anonymous两种)。
GDI对象和核心对象之间有一个主要的不同。GDI对象有单一拥有者,不是进程就是线程。核心对象可以有一个以上的拥有者,甚至可以跨进程。为了保持对每一位拥有者的追踪,核心对象保持了一个引用计数,以记录有多少handles对应到此对象。对象中也记录了哪一个进程或线程是拥有者。如果调用CreateThread()或是其他会传回handle的函数,引用计数便累加1。当你调用CloseHandle()时,应用技术便递减1。一旦引用计数降至0,这一核心对象即自动被摧毁。
4. 释放核心对象
BOOL CloseHandle(HANDLE hObject);
参数:hObject 代表一个已打开之对象handle。
工作者线程:是指完全不牵涉图形用户界面(GUI),纯粹做运算的线程。
GUI线程:拥有消息队列的线程,任何一个特定窗口的消息总是被产生着一窗口的线程抓到并处理。所有对此窗口的改变也都应该由该线程来完成。
不可以依赖“因线程的结束而清理所有被这一线程产生的核心对象”,许多对象,是被进程拥有,在进程结束之前不能够清理它们。
5. 线程结束代码(Exit Code)
BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode);
参数:hThread:由CreateThread()传回的线程handles
lpExitCode:指向一个DWORD,用以接受结束代码(exit code)。
6. 结束线程
使用强制性的收发结束一个线程,ExitThread()。
VOID ExitThread(DWORD dwExitCode);
参数:dwExitCode:指定此线程之结束代码。
任何代码放在此行之下,保证不会被执行,相当于C runtime library中的exit()函数。
7. 主线程
程序启动后就执行的那个线程称为住线程(primary thread)。主线程有两个特点:(1)它必须负责GUI程序中的主消息循环,(2)这一线程的结束会使得程序中的所有线程都被强迫结束,程序也因此结束,其他线程没有机会做清理工作。
8. 错误处理
可以使用名为MTVERIFY的宏,这个宏内部其实是记录并解释Win32 GetLastError()的结果。