[.net]线程基础

关于线程的诞生

  早期的16位Windows只有一个执行线程,在执行各种程序时,如果这个线程运行出现了问题,就会“冻结”整个系统,使得系统处于未响应状态。这是一件多么尴尬的事儿,无论是用户还是微软自己,都不能长时间的忍受这种状况!不过,在那个时代,能有一台电脑,能使用Windows本身就是一件极其奢侈和有乐趣的事儿,也许用户还是能接受这种状况的。不过随着芯片技术的发展,微软是绝对不能安于现状的,所以必须设计一个健壮、可靠、易于伸缩和安全的系统,以便于和新的芯片搭配,所以微软推出了Windows NT,第一次搭载了全新的内核,后续的Windows版本,都是基于这个新的内核。那么这个内核和线程有什么关系呢?在新的系统内核中,操作系统管理多个进程,每个进程又管理这多个线程。

  在进程中运行每个应用程序的实例。而进程是应用程序实例要使用的资源的集合,每一个进程都有一个独立的虚拟地址空间,所以进程之间不能访问各自的代码和数据,当然也不能访问OS本身的数据。这就保证了系统运行的安全。可是,早期计算机只有一个CPU,如果CPU执行陷入死循环,那么系统任然会停止响应。为了解决这个问题,微软拿出了一个解决方案——线程。让每个进程拥有各自的线程。线程的职责就是对CPU进行虚拟化,对于应用程序来说,实际上一个线程相当于一个CPU,把所有的代码执行任务交给线程,如果某个线程执行出错,那么只有与这个线程关联的进程会挂掉,其他的进程不受影响,在OS的控制下,它们继续很好的执行!

线程究竟包含什么?

  现在才是干货。在Windows中,每一个线程都会有这么几个要素。

  ·线程内核对象(Thread Kernel Object):这是线程非常重要的一个数据结构,它包含了一些对线程描述的属性,而且包含线程上下文(thead context)。上下文表示的是线程的执行信息,这些信息分别被保存在CPU的寄存器上。线程切换的时候,首先会保存当前线程的上下文信息,然后切换到另一个线程,最后恢复另外一个线程的上下文信息到实际的CPU寄存器中。

  ·线程环境块(thread environment block,TEB):当线程执行进入一个try块(try{}catch{}用于捕获异常)的时候,就会在TEB中记录一个节点。与此同时,TEB还包含了一些图形接口相关的数据结构。

  ·用户模式栈(user-mode stack):线程执行中的局部变量,方法调用参数都被存储在这儿。而且还指出了在当前线程执行的方法结束后,线程应该回到什么地方接着执行,类似中断过程。关于用户模式栈,是一个线程消耗系统内存资源的大户。为什么这么说呢?之前提到的 线程内核对象 和 TEB 它们的大小只有几kb,最小都不足1kb,但是Windows 给 用户模式栈 的初始大小就是1MB,而且随着实际需要,系统会调拨更多的物理内存给它。也许你会觉着不就1MB嘛,我内存是8GB的。那你就年轻了,然而就在笔者写这篇笔记的时候,我的Windows 10 系统有121个进程,有2100个线程。粗略计算,至少有2100x1MB的内存空间用于创建线程了,也就是2GB。

  ·内核模式栈(kernal-mode stack):应用程序调用内核模式函数传参时使用。为什么会有内核模式栈出现呢?其实还是为了安全,用户调用内核模式函数的参数会被从用户模式栈复制到内核模式栈,然后它的功能就和用户模式栈则差不多了。简单理解就是内核模式栈服务于系统内核方法,用户模式站服务于用户自己的方法。

  ·DLL线程连接和线程分离通知:再Windows中有一个策略,再进程中创建线程时,会遍历当前进程加载的所有非托管DLL的DllMain方法。然后传递一个attach标记。所以创建一个线程会有一定的性能损耗。现在一个常用的的应用,都会加载N多的Dll。例如Vs2015,它在运行的时候至少会加载三四百个DLL,它们中有许多是非托管的Dll,遍历用这些Dll的入口函数也是一件不小的工作。不过值得高兴的一点是Windows 提供了一个Win32 方法DisableThreadLibraryCalls,非托管的Dll调用这个方法,就可以不去理会线程连接的通知,但是,许多的非托管Dll的程序员都不知道这个函数的存在,这就很尴尬了!

  以上五点就是一个线程会包含的基本要素了。线程很好,因为它可以让我们的程序在表面上并行运行,但是滥用线程可就不好了!物极必反。创建一个线程会对系统的运行造成一定的性能损耗,虽然这个损耗可能很小很小,但是会由量变到质变。在一个进程中,每一时刻都只能有一个确定的线程执行,然而N多的线程是通过不断切换执行的,Windows大概每30ms就回执行一次线程切换,正如前面所说的,在线程切换的时候,要经历一个保存线程上下文—切换线程—加载线程上下文的过程。如果线程切换的频率很低,必然没有什么问题,但是在高频率的线程切换中,会影响系统性能。Cpu在读取数据时,从高速缓存cache中读取的速度远大于内存ram,而执行线程上下文切换,会导致原本在cache中的数据丢失,需要重新从ram中读取数据。这就是影响性能的地方。
  目前为止,在一个单核cpu上,确定时刻只能由一个线程执行。直到多CPU计算机、多核CPU的出现,才能真正实现在同一时刻,有多个线程同时执行。而多CPU的计算机由于成本,功耗等各种原因,使用的并不多。真正得到普及的是多核CPU。试想,在多核CPU上,最完美的状态就是CPU有多少个核心,就创建多少个线程,这样就不会有线程切换,性能得到了保障。实际在一个Windows系统中,同时会有上千个线程。美好的理想就这么残忍的被现实打败了!但是在多核CPU普及的今天,我们的确从其多核心的计算中受益。

  线程切换的时候,需要考虑一个问题:在一个进程中,有许多的线程,如何选择切换对象呢?在Windows中,引入了线程优先级的概念。Windows 将这种优先级量化为32个级别,数值越大,优先级越高,数值从0到31。切换时会优先切换高优先级的线程。但,问题又出现了,开发者并不能很好的掌握线程的优先级应该设置为多少。为了解决这个问题,Windows又引入了一个优先级抽象层,有两个概念“进程优先级类“和“相对线程优先级”。进程优先级类针对每一个进程,分为idle,below normal,normal,above normal,high,realtime六个级别,而相对线程优先级则分为idle,lower,below normal,normal,above normal,highest,time-critical七个级别,他们的不同组合对应了32个数字优先级。具体看图

  idle lowest below normal normal above normal highest time-critical
idle 1 2 3 4 5 6 15
below normal 1 4 5 6 7 8 15
normal 1 6 7 8 9 10 15
above normal 1 8 9 10 11 12 15
high 1 11 12 13 14 15 15
realtime 16 22 23 24 25 26 31

.Net中的线程
  前后台线程:在.net中创建的线程分为前后台线程,前台线程如果没有结束,则整个应用程序就不能结束,依然会暂留在进程中,直到前台线程结束。而与之对应的后台线程则不同,一旦应用程序终止,后台线程随即终止,无论是否完成。我们可以通过System.Threading这个命名空间下的类来操作线程。而通过直接实例化一个Thead对象来创建的线程,默认是一个前台线程,可以通过IsBackground属性将其设置为后台线程。但是讲道理,平时使用的线程中,一般都是后台线程,通过线程池创建的线程都是后台线程。

  多线程编程无论在什么编程语言中,都是一个重要的话题,而以上这些,只是.net中多线程编程的开始!

声明:笔记中内容均参考自Jeffrey Richter著作的《CLR Via C#》,个人也很看同作者的一些观点,像大神致敬!

时间: 2024-10-11 11:45:35

[.net]线程基础的相关文章

线程基础知识

什么是线程: 在一个程序里的一个执行路线就叫做线程(thread).更准确的定义是:线程是"一个进程内部的控制序列" 一切进程至少都有一个执行线程 进程与线程 进程是资源竞争的基本单位 线程是程序执行的最小单位 线程共享进程数据,但也拥有自己的一部分数据 线程ID 一组寄存器 栈 errno 信号状态 优先级 fork和创建新线程的区别 当一个进程执行一个fork调用的时候,会创建出进程的一个新拷贝,新进程将拥有它自己的变量和它自己的PID.这个新进程的运行时间是独立的,它在执行时几乎

线程基础:多任务处理(13)——Fork/Join框架(解决排序问题)

============== 接上文< 线程基础:多任务处理(12)--Fork/Join框架(基本使用)> 3. 使用Fork/Join解决实际问题 之前文章讲解Fork/Join框架的基本使用时,所举的的例子是使用Fork/Join框架完成1-1000的整数累加.这个示例如果只是演示Fork/Join框架的使用,那还行,但这种例子和实际工作中所面对的问题还有一定差距.本篇文章我们使用Fork/Join框架解决一个实际问题,就是高效排序的问题. 3-1. 使用归并算法解决排序问题 排序问题是

线程基础:线程池(6)——基本使用(中)

(接上文:<线程基础:线程池(5)--基本使用(上)>) 3-4.JAVA主要线程池的继承结构 我们先来总结一下上文中讨论过的内容,首先就是JAVA中ThreadPoolExecutor类的继承结构.如下图所示: ThreadPoolExecutor:这个线程池就是我们这两篇文章中介绍的重点线程池实现.程序员可以通过这个线程池中的submit()方法或者execute()方法,执行所有实现了Runnable接口或者Callable接口的任务:ThreadPoolExecutor对于这些任务的执

线程基础--线程控制

3.  线程控制 1). 线程属性 目标:可以设置 线程的 detached/join 状态,线程栈的大小和最低地址等属性. detached/join 状态的区别: 当线程处于 分离状态(detached)时,线程结束时,os立即回收资源.主线程不可以调用pthread_join获取线程退出时的返回值. 当线程处于 未分离状态(join)时,线程结束时,主线程 调用pthread_join获取线程退出时的返回值, 随后释放该线程资源. a)数据类型 pthread_attr_t b)初始化及释

操作系统核心原理-4.线程原理(上)线程基础与线程同步

我们都知道,进程是运转中的程序,是为了在CPU上实现多道编程而发明的一个概念.但是进程在一个时间只能干一件事情,如果想要同时干两件或者多件事情,例如同时看两场电影,我们自然会想到传说中的分身术,就像孙悟空那样可以变出多个真身.虽然我们在现实中无法分身,但进程却可以办到,办法就是线程.线程就是我们为了让一个进程能够同时干多件事情而发明的“分身术”. 一.线程基础 1.1 线程概念 线程是进程的“分身”,是进程里的一个执行上下文或执行序列.of course,一个进程可以同时拥有多个执行序列.这就像

线程基础(一)

2015年自己定了个目标就是开始写写博客,所以心血来潮,我也来写写一些基础的东西. 线程,网上大把多资料,自己写也不会有什么新意.不过在自己做过的一些项目中用到很多线程相关的知识,但没有真正的去总结,去归纳的,导致到了用的时候有baidu.google(google被我朝禁了...我觉得是一种悲哀).好了,下面开始我的线程基础了 1.线程的定义 线程只由相关堆栈(系统栈或用户栈)寄存器和线程控制表TCB组成.它是系统调度的最基本单位.个人的理解是线程相当于开辟一个单元去执行特定的方法过程,就涉及

C#之线程基础概述

1.概述: 每个正在操作系统上运行的应用程序都是一个进程,一个进程可以包括一个或多个进程.线程是操作系统分配处理器时间的基本单元. 在进程中可以有多个线程同时执行代码,每个线程都维护异常处理程序.调度优先级和一组系统用于在调度该线程前保存线程上下文的结构. 线程上下文包括为使线程在线程的宿主进程地址空间中无缝地执行所需的所有信息,包括线程的CPU寄存器组和堆栈. 线程是进程中的执行流程,一个进程中可以同时包括多个线程,每个线程也可以得到一小段程序的执行时间,这样一个进程就可以具有多个并发执行的线

Android多线程研究(1)——线程基础及源代码剖析

从今天起我们来看一下Android中的多线程的知识,Android入门easy,可是要完毕一个完好的产品却不easy,让我们从线程開始一步步深入Android内部. 一.线程基础回想 package com.maso.test; public class TraditionalThread { public static void main(String[] args) { /* * 线程的第一种创建方式 */ Thread thread1 = new Thread(){ @Override p

Qt 线程基础(QThread、QtConcurrent等) 2

使用线程 基本上有种使用线程的场合: 通过利用处理器的多个核使处理速度更快. 为保持GUI线程或其他高实时性线程的响应,将耗时的操作或阻塞的调用移到其他线程. 何时使用其他技术替代线程 开发人员使用线程时需要非常小心.启动线程是很容易的,但确保所有共享数据保持一致很难.遇到问题往往很难解决,这是由于在一段时间内它可能只出现一次或只在特定的硬件配置下出现.在创建线程来解决某些问题之前,应该考虑一些替代的技术 : 替代技术 注解 QEventLoop::processEvents() 在一个耗时的计

线程基础知识系列(三)线程的同步

本文是系列的第三篇,前面2篇,主要是针对单个线程如何管理,启动等,没有过多涉及多个线程是如何协同工作的. 线程基础知识系列(二)线程的管理 :线程的状态,控制,休眠,Interrupt,yield等 线程基础知识系列(一)线程的创建和启动  :线程的创建和启动,join(),daemon线程,Callable任务. 本文的主要内容 何谓线程安全? 何谓共享可变变量? 认识synchronized关键字 认识Lock synchronized vs Lock 1.何谓线程安全 多线程是把双刃剑,带