线程的概念
线程的职责是对CPU进行虚拟化。
CPU为每个进程都提供了该进程专用的线程(功能相当于cpu),应用程序如果进入死循环,那么所处的进程会"冻结",但其他进程不会冻结,它们会继续执行!
线程的开销
因为是虚拟化CPU,所以也会有空间(内存耗用)和时间(执行性能)上的开销。
具体的开销:
从上面这些开销可以看出,创建和销毁一个线程的开销虽然没有进程那么大,但是也不小了。
甚至减少线程的数量还会提高垃圾回收的性能,因为垃圾回收时会挂起所有线程。
线程的切换也会有性能损失:
单CPU计算机一次只能做一件事情,所以所有的线程实际上是共享物理CPU的。多个CPU的计算机或者多核CPU,可以真正同时运行几个线程。然而单个线程还是只能在一个内核上运行。
任何时刻,一个CPU都只会被分配给一个线程。那个线程占用CPU一段时间后(叫做时间片),就会切换到另一个线程。(如果时间片结束后,Windows决定再次调用同一线程,那么不会执行线程切换)
线程切换大概每30毫秒进行一次。
每次线程切换时进行的操作:
线程切换虽然消耗性能,但是却提供了一个健壮灵敏的操作系统。如果某线程进入死循环,不会影响其它线程。
线程在等待IO操作,会使线程进入等待状态,让线程在任何CPU上都不再调度,直到发生下一次输入事件。
使用专用线程执行异步的计算限制操作
以下介绍使用专用线程执行异步的计算限制操作,但是建议避免使用此技术,而用线程池来执行异步的计算限制操作。
如果执行的代码要求线程处于一种特定的状态,而这种状态对于线程池线程来说是非同寻常的,就可以考虑创建专用线程。
例如:
一个简单的使用专用线程执行异步操作的例子:
static void Main(string[] args) { Thread 某线程 = new Thread(线程回调函数); 某线程.Start("hello"); Console.WriteLine("某线程运行开始"); 某线程.Join();//join方法造成调用线程阻塞当前执行的任何代码,直到“某线程”销毁或者终止 Console.WriteLine("继续运行"); Console.Read(); } private static void 线程回调函数(Object 状态参数) { Thread.Sleep(10000); if (状态参数.GetType() == typeof(string)) { Console.WriteLine("这是一个字符串"); } else { Console.WriteLine("未识别"); } }
使用线程的理由
线程调度和优先级
抢占式操作系统必须使用算法去判断什么时候调度哪些线程多长时间。
前面提到过,每个线程都包含一个线程内核对象,而内核对象中包含一个上下文结构,此结构中存储了线程上一次执行完毕后CPU寄存器的状态。
在一个时间片完后,windows会检查现存的所有线程内核对象,在这些对象中,只有那些没有正在等待什么的线程才适合调度。
Windows选择一个可调度的线程内核对象,并上下文切换到它。
然后线程开始执行代码,并在其进程的地址空间处理数据。然后过了一个时间片完后又循环执行以上操作。
Windows从系统启动开始便一直执行上下文切换,直到系统关闭为止。
之所以被称为抢占式操作系统,是因为线程可以在任何时间停止(被抢占)并调度另一个线程。
每个线程都分配了从0(最低)到31(最高)的优先级。系统决定为CPU分配哪个线程时,首先检查优先级为31的线程,并以一种轮流的方式调度它们。
只要还存在优先级高的可调度线程,那么就不会将优先级低的线程分配给CPU。这种情况称为饥饿。
系统启动时会创建一个特殊的零页线程,其优先级为0,而且是整个系统中唯一优先级为0的线程。在没有其它线程需要工作的时候,零页线程会将系统RAM的所有空闲页清零。
将优先级设为数字,实际操作中很难分配合理,于是微软给了一个更简单的方法。
在设计应用程序时,可以选择一个进程优先级类(可以选择Idle,Below Normal,Normal,Above Normal,High和Realtime),默认的Normal为最常见的优先级类。
事实上进程优先级类只是一个抽象的概念,Windows永远不会调度进程,只会调度线程。
在系统中什么都不做的时候运行的应用程序如屏保程序适合分配Idle优先级类。
而RealTime优先级优先级太高,甚至可能影响到操作系统任务,可能造成不能及时地处理键盘和鼠标输入。
而Windows还支持7个相对线程优先级(Idle,Lowest,Below Normal,Normal,Above Normal,Highest和Time-Critical),它们和进程优先级类一起确定最后的线程优先级。
最好是降低一个应用程序的优先级而不是提高另一个线程的优先级。高优先级的线程大多数时候应该使其保持为等待状态。
而我们可以通过设置Thread的Priority,向其传送ThreadPriority枚举类型定义的5个值之一:Lowest,Below Normal,Normal,Above Normal,Highest。
没有Idle和Time-Critical是因为CLR保留了。之前在垃圾回收那里提到的CLR的终结器线程以Time-Critical优先级运行。
如果应用程序以特殊的安全权限运行,可以使用System.Diagnostics命名空间中的Process和ProcessThread类,这两个类分别提供了进程和线程的Windows视图。
应用程序也可以使用AppDomain和Thread类,它们公开了AppDomain和线程的CLR视图。(这两个类不需要特殊的安全权限,只有部分操作需要)
前台线程和后台线程
CLR将线程分为前台线程和后台线程。
一个进程的所有前台线程停止运行时,CLR强制终止仍在运行的任何后台线程,并且不抛出异常。
static void Main(string[] args) { Thread 某线程 = new Thread(线程回调函数); 某线程.IsBackground = true; 某线程.Start("hello"); Console.WriteLine("某线程运行开始"); Console.WriteLine("继续运行"); } private static void 线程回调函数(Object 状态参数) { Thread.Sleep(10000); if (状态参数.GetType() == typeof(string)) { Console.WriteLine("这是一个字符串"); } else { Console.WriteLine("未识别"); } }
如果某线程为前台线程,那么应用程序在10秒后才终止,如果是后台线程,那么应用程序立即终止。
原因是如果是前台线程,那么在运行完Main函数后,还需要这个前台线程结束才终止应用程序,而如果转为后台线程,那么就会在所有前台线程终止后立马终止。
通过Thread对象来显示创建线程的都是前台线程,但是可以通过IsBackgroun属性来切换,而线程池都是后台线程。
原文:http://www.cnblogs.com/vvjiang/p/5457052.html