进程与线程杂谈(一)
--转载
我了解不多,只能说说80x86上的Windows环境,其他如Alpha,Mac或者Linux,
我就一窍不通了,见笑见笑……
现代操作系统都是多任务的操作系统,在这里要澄清一个概念,Windows 3.x时代也
有所谓的多任务,但是,那并不是现代意义的多任务--"抢占式多任务"。Windows 3.x的
多任务是非抢占式的,即,一个应用程序,甚至系统,要等现在正在运行的程序主动放弃
CPU(程序员把这个礼貌的行为写到程序中),才能获得执行时间片。可以看出,在这种环
境中,操作系统的主动性是很小的,只要某个已经获得CPU的程序不主动放弃CPU,操作
系统就得不到时间运行,亦无法实行其管理调度功能。如果一个应用程序因崩溃而挂起,
将导致整个系统挂起。
而抢占式的多任务是指,将CPU时间片分配最需要它的应用程序,即便一个应用程序永
远不打算放弃CPU,操作系统也能保证随时"抢占"CPU时间,然后对当前所有的任务进行
合理的调度。
要实现多任务,首先要有CPU的支持,80x86 (Alpha?我不懂,别问我……) 支持
多任务,通过一个TSS--任务描述符,80x86能够描述一个任务。详细我就不说了,和本
文关系不大,只要说明抢占式多任务不是操作系统在单干就行了。
作为操作系统,要能够利用CPU提供的功能才能实现多任务,Windows做到了这点(当
然,Linux也做到了,只是我不懂……)。系统有一个核心调度程序,负责为每一个任务分配
CPU时间,允许其执行指定的一段时间,当这段时间用完后,控制权会重新交回到操作系统,
操作系统可在此时重新分配CPU时间。至于CPU时间是如何分配的,就又不是本文的范围了。
Microsoft给出的文档是说"系统保证CPU时间的分配是公平的",仅此而已……。
在Windows中,一个任务就是一个线程,以前曾经说过,线程不能没有存储而存在,
而其所依赖存在的存储对象就是进程。而一个进程--以前也说过了--对应一个映象(可执行
文件)。也就是说,在一个可执行文件运行时,可以有多个线程。
好了,基本的概念大概说明了,来讨论一下它们的用途。要这些多出来(除了原始线程
外)的线程有什么用?MSDN给出的答案是"等"。但我个人认为稍微笼统了一点(偏激一点?
)。不过,至少,有一点是肯定的--绝不要使多个线程同时进行繁重的操作--除非你是建立
了CPU数目个线程(32路对等处理器系统?)。无论如何,实际工作的还是CPU,在这种情
况下,CPU不仅不会少执行指令,还要执行很多的排班程序指令以及任务的切换指令--反而
降低效率。
在需要等待的地方,多线程确实能够发挥很高的效能。(注意,并不一定是最高,以后
将说到经常是更好的实现方法,这里只是说明线程的用法)举一个例子:一个网络程序向远
程主机发送了一个请求,正在等待回应,而在此期间,它还希望能够与用户进行交互。一种
实现方法是:程序继续与用户交互,在交互的间歇检查一下回应是否到达。而更好的方法是
建立一个新的线程(称为工作线程)来等待回应,原始线程继续照常与用户交互。如果您已
经感觉到后一种方法确实比前一种方法好很多,那么,您可以不读下一段,不用听我罗嗦了。
后一种方法比前一种方法好在后者执行的指令更少,因而效率更高。如果您对Windows
编程熟悉:在实现前者时,必须保证能够及时检测到回应到达,因而就不能使用GetMessage()
而要使用PeekMessage()。如果使用GetMessage(),而恰巧在很长的一段时间内都没
有消息到达,原始线程就不会从GetMessage()返回,也就不能检测回应是否到达。使用
PeekMessage(),可以令其在没有消息时也立即返回,因而可以检测回应是否到达。网络
部分的情况也一样--程序不能等回应一直等下去否则就无法与用户交互。无论如何,在即没
有消息、也没有回应到达的情况下,原始线程没有有效的进入等待状态,而是不停地"空转",
检测二者中是否有到达的,这对系统资源显然是极大的浪费。而对于后者,原始线程通过
GetMessage()有效的进入了等待,工作线程也可以通过类似的方法进入等待,例如使用
Socket的select()。
但是,新的问题又出现了,工作线程发现回应已经到达了,它可能要通知原始线程才行
--它与原始线程是并发执行的。这就涉及到线程简通信了,有一种最简单的方法:Windows
消息。工作线程通过向原始线程的窗口发送一个消息,然后终止;当原始线程的消息循环发
现这个消息时,就知道回应已经到达了。线程间通信的方法还有很多,将在以后专门介绍。
最后要说明的是--很重要的一点:多线程经常不是程序员主动来使用的,而是在依赖操
作系统时,已然是多线程了。即使用,也很有节制,滥用线程将适得其反。究竟什么地方应
该使用多线程,并没有什么规则,将多线程用在该用的地方而已,当然不会刻意的使用它。
当综合各种条件和各种可能的方案后,如发现多线程是最好的,就是使用多线程的最佳时机。
我是否说的是废话?呵呵,其实就像这篇文章一样,我也很头疼,线程是在遇到实际需要时
才想起用的,硬要设计一种情况来使用实在不易。有备无患。多线程不是杀手锏,但是掌握
它是向较高级编程迈进的必经之路。