25.8 使用线程的理由
三个方面的理由:
- 可以使用线程将代码同其他代码隔离 由于你的应用程序对健壮性的要求没有操作系统对健壮性的要求高,所以你的应用程序不应出于维持健壮性的目的而使用太多的线程。如果你的应用程序支持加载由其他厂商生成的组件,那么应用程序对健壮性的要求就会提高,使用线程有助于满足这个需求。
- 可以使用线程来简化编码 有的时候,如果通过一个任务自己的线程来执行该任务,编码会变得更简单。
- 可以用线程来实现并发执行。
抢占式(preemptive)操作系统必须使用某种算法判断在什么时候调度哪些线程多长时间。
Windows之所以被称为一种抢占式多线程(preemptive multithreaded)操作系统,是因为线程可以在任何时间停止(被抢占),并调度另一个线程。
每个线程都分配了0(最低)~31(最高)的一个优先级。
只要存在可以调度的优先级31的线程,系统就永远不会将优先级0~30的任何线程分配给CPU。这种情况称为饥饿(starvation)。当较高优先级的线程占用了太多CPU时间,致使较低优先级的线程无法运行时,就会发生这种情况。
较高优先级的线程总是抢占较低优先级的线程,无论正在运行的是什么较低优先级的线程。
系统启动时,会创建一个名为零页线程(zero page thread)的特殊线程。这个线程的优先级定位0,而且是整个系统中唯一一个优先级为0的线程。零页线程负责在没有其他进程需要执行的时候,将系统RAM的所有空闲页清空。
设计应用程序时,应决定自己的应用程序是需要比机器上同时运行的其他应用程序更大还是更小的响应能力。然后,选择一个进程优先级类(priority class)来反映你的决定。Windows支持6个进程优先级类:Idle,Below Normal,Normal,Above Normal,High和Realtime。由于Normal是默认优先级类,所以它是最常用的优先级类。
Windows支持7个相对线程优先级:Idle,Lowest,Below Normal,Normal,Above Normal,Highest和Time-Critical。这些优先级是相对于进程优先级类的。同样地,由于Normal是默认的相对线程优先级,所以是最常用的。
线程优先级是相对于进程优先级类的,如果更改一个进程的优先级类,线程的相对优先级不会改变,但它的优先级值会改变。
0优先级保留给零页线程了,系统不允许其他线程的优先级为0。
Windows永远不会调度进程;它调度的只有线程。“进程优先级类”是Microsoft提出的一个抽象概念,旨在帮助你理解自己的应用程序和其他正在运行的应用程序的关系,它没有别的用途。
正常情况下,进程根据启动它的进程来分配一个优先级。大多数进程都是由Windows资源管理器启动的,后者在Normal优先级类中生成它的所有子进程。托管应用程序不应该表现为拥有它们自己的进程;相反,它们应该表现为在一个AppDomain中运行。所以,托管应用程序不应该更改它们的进程的优先级类,因为这会影响进程中运行的所有代码。
你的应用程序可以更改它的相对线程优先级,这需要设置Thread的Priority属性,向它传递ThreadPriority枚举类型中定义的5个值之一,即Lowest,BelowNormal,Normal,AboveNormal或者Highest。然而就像Windows为自己保留了优先级0和Realtime范围一样,CLR为自己保留了Idle和Time-Critical优先级。今天的CLR还没有以Idle优先级运行的线程,但这一点将来可能发生改变。CLR的终结器线程以Time-Critical优先级运行。
应该指出的是,System.Diagnostics命名空间包含一个Process类和一个ProcessThread类,这两个类分别提供了进程和线程的Windows视图。如果要用托管代码写工具(utility)应用程序,或者想构建代码来帮助自己对其进行调试,就可以使用这两个类。事实上,这正是为什么这两个类在System.Diagnotics命名空间中的原因。应用程序需要以特殊的安全权限才能使用这两个类。
应用程序可使用AppDomain和Thread类,它们公开了AppDomain和线程的CLR视图。一般不需要特殊安全权限来使用这两个类,虽然某些操作仍需要提升权限才可以。