你的位置:首页 > 软件开发 > ASP.net > 【C#进阶系列】25 线程基础

【C#进阶系列】25 线程基础

发布时间:2016-05-04 02:00:04
线程的概念线程的职责是对CPU进行虚拟化。CPU为每个进程都提供了该进程专用的线程(功能相当于cpu),应用程序如果进入死循环,那么所处的进程会"冻结",但其他进程不会冻结,它们会继续执行!线程的开销因为是虚拟化CPU,所以也会有空间(内存耗用)和时间(执行性 ...

get='_blank'>线程的概念

线程的职责是对CPU进行虚拟化。

CPU为每个进程都提供了该进程专用的线程(功能相当于cpu),应用程序如果进入死循环,那么所处的进程会"冻结",但其他进程不会冻结,它们会继续执行!

线程的开销

因为是虚拟化CPU,所以也会有空间(内存耗用)和时间(执行性能)上的开销。

具体的开销:

  • 线程内核对象(thread kernel object)
    • 操作系统为创建的每个线程都会分配并初始化这种数据结构。数据结构包含一组对线程进行描述的属性,还包含线程的上下文,包含模拟CPU寄存器的集合的内存块。
  • 线程环境块(thread environment block,TEB)
    • TEB是在用户模式下分配和初始化的内存块。
    • TEB包含线程的异常处理链首。线程进入每个try块都在链首插入一个节点,线程退出try块从链中删除该节点。
    • TEB还包含线程的“线程本地存储”数据,以及由GDI和OpenGL图形使用的一些数据结构。
  • 用户模式栈(user-mode stack)
    • 用户模式栈存储传给方法的局部变量和实参。包含一个地址,指出当前方法返回,线程应该从什么地方接着执行。
    • Windows默认为每个线程的用户模式栈分配1MB的空间。 
  • 内核模式栈(kernel-mode stack)
    • 应用程序代码向操作系统中的内核模式函数传递实参时,还会使用内核模式栈。针对传给内核的任何实参,都会从用户模式栈复制到内核模式栈。
  • DLL线程连接(attach)和线程分离(detach)通知
    • 任何时候在进程中创建线程,都会调用进程中加载的所有非托管DLL的DLLMain方法,并向该方法传递DLL_THREAD_ATTACH标志。
    • 任何时候在进程中线程终止,都会调用进程中加载的所有非托管DLL的DLLMain方法,并向该方法传递DLL_THREAD_DETACH标志

从上面这些开销可以看出,创建和销毁一个线程的开销虽然没有进程那么大,但是也不小了。

甚至减少线程的数量还会提高垃圾回收的性能,因为垃圾回收时会挂起所有线程。

线程的切换也会有性能损失:

单CPU计算机一次只能做一件事情,所以所有的线程实际上是共享物理CPU的。多个CPU的计算机或者多核CPU,可以真正同时运行几个线程。然而单个线程还是只能在一个内核上运行。

任何时刻,一个CPU都只会被分配给一个线程。那个线程占用CPU一段时间后(叫做时间片),就会切换到另一个线程。(如果时间片结束后,Windows决定再次调用同一线程,那么不会执行线程切换)

线程切换大概每30毫秒进行一次。

每次线程切换时进行的操作:

  1. 将CPU寄存器的值保存到当前正在运行的线程的线程内核对象(前面提到过)内部的一个上下文结构。
  2. 从现有线程集合中选出一个线程供调度。如果该线程由另一个进程拥有,Windows在开始执行任何代码或者接触任何数据之前,还必须切换CPU“看见”的虚拟地址空间。
  3. 将切换到的新线程中,线程内核对象中的模拟寄存器的值加载到CPU的寄存器中

线程切换虽然消耗性能,但是却提供了一个健壮灵敏的操作系统。如果某线程进入死循环,不会影响其它线程。

线程在等待IO操作,会使线程进入等待状态,让线程在任何CPU上都不再调度,直到发生下一次输入事件。

使用专用线程执行异步的计算限制操作

以下介绍使用专用线程执行异步的计算限制操作,但是建议避免使用此技术,而用线程池来执行异步的计算限制操作。

如果执行的代码要求线程处于一种特定的状态,而这种状态对于线程池线程来说是非同寻常的,就可以考虑创建专用线程。

例如:

  • 线程需要以非普通线程优先级运行。而所有线程池都以普通优先级运行;虽然可以更改这一优先级,但不建议这么做。并且在不同的线程池操作之间,优先级的更改是无法持续的
  • 需要线程表现为一个前台线程,防止应用程序在线程任务结束前终止。线程池现场呢个始终为后台线程。如果CLR想终止进程,那么它们就完成不了任务。
  • 计算限制的任务需要长时间运行。线程池为了判断是否需要创建一个额外的线程,所采用的逻辑是比较复杂的。直接为长时间运行的任务创建专用线程,就可以避免这一问题。
  • 要启动线程,并可能调用Thread的Abort方法来提前终止它。

一个简单的使用专用线程执行异步操作的例子:

     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("未识别");      }    }

原标题:【C#进阶系列】25 线程基础

关键词:C#

C#
*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: admin#shaoqun.com (#换成@)。

可能感兴趣文章

我的浏览记录