第5章 不要让线程成为脱缰的野马(Keeping your Threads on Leash) ----初始化一个线程
使用线程的一个常见问题就是如何能够在一个线程开始运行之前,适当地将它初始化。初始化最常见的理由就是为了调整优先权。另一个理由是为了在SMP 系统中设定线程比较喜欢的 CPU。第10 章谈到 MFC 时我们会看到其他一些理由。
基本问题在于,你需要一个线程 handle,才能够调整线程的性质。但如果你以默认型式调用 CreateThread(),新线程会如脱缰野马一下子就起跑了,你根本来不及进行初始化设定操作。
解决之道就是 CreateThread() 的第5个参数,它允许你指定线程诞生时的属性。目前只定义有一种属性,就是 CREATE_SUSPENDED。这个属性告诉CreateThread() 说:产生一个新线程,传回其 handle,但不要马上开始执行之。
下面是一个例子:
HANDLE hThread;
DWORD threadId;
hThread = CreateThread(NULL,
0,
ThreadFunc,
0,
CREATE_SUSPENDED,
&threadId
);
SetThreadPriority(hThread, THREAD_PRIORITY_IDLE);
一旦线程设定妥当,你可以调用 ResumeThread() 开始执行:
DWORD ResumeThread(
HANDLE hThread
);
参数
hThread 欲被再次执行的线程。
返回值
如果函数成功,则传回线程的前一个挂起次数。如果失败,则传回0xFFFFFFFF。GetLastError() 可以获得更详细的信息。
挂起(suspending)一个线程
相对于 ResumeThread(),毫不令人惊讶地,有一个 SuspendThread() 函数。这个函数允许调用端指定一个线程睡眠(挂起)。直到又有人调用了 ResumeThread(),线程才会醒来。因此,睡眠中的线程不可能唤醒自己。这个函数的规格如下:
DWORD SuspendThread(
HANDLE hThread
);
参数
hThread 欲被挂起的线程。
返回值
如果函数成功,则传回线程目前的挂起次数。如果失败,则传回0xFFFFFFFF。GetLastError() 可以获得更详细的信息。
SuspendThread() 是另一个可能会潜在引发问题的函数。考虑一下这种情况:一个进程拥有三个线程A,B,C。线程C正在某个 critical section 内,而线程B正在等它出来。然后,线程A挂起了线程C。在这种情况下,线程C将永远不会离开 critical section,而线程B也就相当于进入了死锁状态。
SuspendThread() 的最大用途就是用来协助撰写调试器。调试器允许在程序员的控制之下,启动或停止任何一个线程。
提要
在这一章中你看到了“结束一个线程”的正确方法,你也看到了线程优先权的定义方式, 及其改变所带来的影响。最后你还看到了如何利用CREATE_SUSPENDED 来初始化一个线程—在它开始执行之前。