25线程基础-CLR
由CLR via C#(第三版) ,摘抄记录...
1、线程是CPU的虚拟化,windows为每个进程提供专用线程(CPU)
2、线程开销:内存和时间。
线程内核对象—OS为系统中创建的每个线程都分配并初始化这种数据结构之一。其中包含对线程进行描述的属性,和上下文。上下文是内存块,x86的是约700字节,x64是约1240,ia64是2500。
线程环境块TEB,用户模式中的内存块(应用程序代码能快速访问的空间),一个TEB一个内存页,x86和x64都是4KB,IA64是8KB。 TEB包含线程的异常处理链首(head)。进入每一个try都在后面插入一个节点,退出try时删除该节点。 TEB还包含线程的本地存储 数据,以及GDI和OpenGL图形使用的数据。
用户模式栈 存储传递给方法的局部变量和实参,还包含一个地址,指出方法返回时,线程从何处执行。默认1MB。
内核模式栈 32位windows下为12KB,64的是24KB。
DLL线程连接和线程分离通知 windows策略决定。。C#和其他托管语言的DLL没有DLLMain函数,不收通知,这提升性能。
3、windows只将一个线程分配给一个CPU,允许其运行一个 时间片 ,上下文切换。
切换:将cpu寄存器的值保存内核对象内部的上下文结构中,选出一个新线程供调度,将其内的上下文结构值加载到CPU。
大约每30毫秒执行一次切换。净开销。而且还会切换高速缓存cache。尽可能避免切换。
时间片结束时,如果windows决定再调度同一线程,就没有切换。这显著改进性能。
4、垃圾回收时,CLR暂停所有线程,遍历他们的栈进行标记,再遍历栈,再恢复所有线程。所以,减少线程数量会显著提升垃圾回收器的性能。调试时也挂起,线程越多,调试体验越差。
单个线程不会在多个内核CPU上调度,应在确保响应能力的同时创建尽量少的线程。理性使用线程。
5、超线程CPU,芯片中包含2组架构状态,只有一组在执行,对于windows,看起来像是2个CPU,所有windows会同时调度2个线程。芯片一次只能执行一个。
6、NUMA架构机器,(略)
7、CLR线程和windows线程: CLR使用的是windows的线程处理能力。一个CLR线程直接对应一个windows线程。
8、使用专用线程执行异步的计算限制操作。
建议避免采用,应尽量使用CLR的线程池来执行异步计算限制操作。
显示创建自己线程的条件:
优先级高,不建议更改线程池的优先级;
表现为一个前台线程,防止应用程序在线程结束它的任务之前终止。线程池中的是后台线程。
一个计算限制的任务需要长时间运行。
要调用Abort方法提前终止它。
Thread.Join()等待线程终结。
9、使用线程的理由 代码隔离健壮性,简化编码,并发。
观点改变,CPU计算能力富余,应大胆消费。
10、线程调度和优先级
抢占式操作系统,使用算法判断在什么时间调度哪些线程多少时间。
一个时间片后,windows检查现有所有线程内核对象,有资源的适合调度。从spy++可以查看切换的次数。线程在任何时间可以被抢占,windows调度另一个。你不能阻止其他线程的运行。
所以,windows是抢占式操作系统,不是实时操作系统,CLR使托管代码的行为更不实时。比如:DLL的JIT加载,代码的JIT编译,GC无法预测的介入。
线程优先级从0(最低)-31(最高),系统启动时有个为0的零页线程的特殊线程,唯一一个0的线程,在没有其他进程要调度时,将系统RAM所有空闲页清零。
优先级类(priority class) :Idle,Below Normal,Normal,Above Normal,High和Realtime。 优先级类是用于进程的。每个线程的优先级取决于 他所属的进程的优先级类和在该进程内他自身的优先级——俩者合成基础优先级——动态优先级是OS确保线程可响应,不一直饥饿所产生。但是16-31之间的线程,系统不提升他们的优先级。0-15的才提升。
应用开发人员永远不直接处理优先级。Normal的Normal是8,大多数程序都是8的优先级。Windwos永远不会调度进程,只调度线程。进程根据启动它的进程来分配一个优先级。大多数进程都是windows资源管理器启动的,属于Normal。
托管应用程序不应该表现为拥有自己的进程;相反,他们应该表现为在一个AppDomain中运行。 可以更改他的线程相对优先级,设置Thread的Priority属性。System.Diagnostics命名空间包含一个Process类和ProcessThread类,提供进程和线程的windows视图。AppDomain和Thread类,公开了应用域和线程的CLR视图。
11、 前台线程和后台线程
线程在CLR中要么是前台,要么是后台。一个进程中的所有前台停止时,CLR强制终止扔在运行的任何后台线程。--直接终止,不抛出异常。
1 public static void Main() 2 { 3 // 创建一个新线程(默认为前台线程) 4 Thread t = new Thread(Worker); 5 6 Console.WriteLine(t.IsBackground.ToString()); 7 // 使线程成为一个后台线程 8 t.IsBackground = true; 9 t.Start(); // 启动线程 10 // 如果 t 是一个前台线程,则应用程序大约 10 秒后才终止 11 // 如果 t 是一个后台线程,则应用程序立即终止 12 Console.WriteLine("Returning from Main"); 13 Console.Read(); 14 15 } 16 private static void Worker() 17 { 18 Thread.Sleep(10000); // 模拟做 10 秒钟的工作 19 // 下面这一行代码,只有在由一个前台线程执行时,才会显示出来 20 Console.WriteLine("Returning from Worker"); 21 22 Console.Read(); 23 } 24 }
在线程生存期内,随时可前后变换。应用的主线程以及通过Thread对象显示创建的都默认前台,线程池默认后台。由进入托管执行环境的本地代码创建的线程被标记为后台。应尽量避免使用前台线程。
wintellect类库,作者写的....
~~~~~~~~~~待续~~
结语:我只想把我所知道的,尽量简洁清楚地表达出来。