C# 线程学习笔记 (1)

基础知识:

一: windows 为什么要支持线程?

 作为一个Windows概念,线程的职责是对CPU进行虚拟化。Windows为每个进程提供了该进程专用的线程(功能相当于一个CPU,可将线程理解为一个逻辑CPU)。如果应用程序的代码进入无限循环,与那个代码关联的进程会“冻结”,但是其他进程不会冻结,他们会继续执行。

二:线程开销

  1. 线程内核对象(thread kernel object)OS为系统中创建的每个线程都分配并初始化这种数据结构之一。在该数据结构中,包含一组对线程进行描述的属性,还包含所谓的数据上下文(thread context)。上下文是一个内存块(包含CPU寄存器集合),X86CPU上下文大小约为700字节,对于X64,上下文约为1240字节。
  2. 线程环境块(thread environment block, TEB)TEB是在用户模式(应用程序代码能快速的访问的地址空间)中分配和初始化的一个内存块。TEB耗用一个内存页(4KB),TEB包含线程的异常处理链首(head)。进程进入的每个try块都在链首插入一个节点。线程退出try时,会从链中删除该节点。除此之外,TEB还包含线程的“线程本地存储”数据,以及由GDI(Graphics Device Interface,图形设备接口)和OpenGL图形使用的一些数据结构。
  3. 用户模式栈(user-mode stack)用户模式占用于存储传递给方法的局部变量和实参。它还包含一个地址;指出当前方法返回的时,线程接着应该从什么地方开始执行。默认情况下,Windows为每个线程的用户模式栈分配1MB内存。
  4. DLL线程连接(attach)和线程分离(detach)通知,Windows的一个策略是,任何时候在进程中创建一个线程,都会调用那个线程中加载的所有DLL的DllMain方法,并向该方法传递一个DLL_THREAD_ATTACH标志。类似地,任何时候一个线程终止,都会调用线程中的所有DLL_THREAD_DETACH标志。有的DLL需要利用这些通知,为进程中创建/销毁的每个线程执行一些特殊的初始化或(资源)清理操作。例如,C-Runtime库DLL会分配一些线程本地存储状态。线程似乎用C-Runtime库中包含的函数时,需要用到这些状态。

任何给定的时刻,Windows只是将一个县城分配给一个CPU。那个线程允许运行一个“时间片”。一旦时间片到期,windows就上下文切换到另外一个线程。每次上下文切换都要求Windwos执行以下操作。

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

三:CLR线程和Windows线程

今天CLR中的线程直接对应Windows线程。Microsoft CLR团队保留了将来把他从Windows县城中剥离出来的权利。

四:线程调度优先级

抢占式操作系统必须使用某种算法判断在什么时候调度那些线程多长时间。上下文(context)结构反映了当线程上一次执行时候,线程的CPU寄存器的状态。在一个时间片(time-slice)之后,Windows检查现有的所有线程内核对象。在这些对象中,只有那些没有正在等待什么的线程才适合调度。Windows选择一个可调度的线程内核对象,兵上下文切换它。Windows实际上记录了每个线程被上下文切换的次数,可以使用工具Microsoft Spy++查看。

设计应用程序的时候,应该决定应用程序是需要比机器上同时运行的其他应用程序更大还是更小的影响能力。然后,选择一个进程优先级类(Priority class)来反映你的决定。Windows目前支持6哥进程优先级类:Idle, Below Normal, Normal, Above Normal, Hight和RealTime。

五:前台线程和后台线程

CLR将每个线程要么看作前台线程要么看作后台线程。一个进程中的所有前台线程停止运行的时候,CLR强制终结仍在运行的任何后台线程。这些后台县城被直接终止;不会抛出异常。

因此,前台线程应该用于执行确实想要完成的任务,比如将数据从内存缓冲区flush到磁盘。另外,应该为非关键代码的任务使用后台线程。

时间: 2024-08-04 13:26:52

C# 线程学习笔记 (1)的相关文章

Java线程学习笔记(二) 线程的异常捕捉

线程异常的捕捉: 正常的情况下,我们在main()方法里是捕捉不到线程的异常的,例如以下代码: public class ExceptionThread implements Runnable{ @Override public void run() { throw new NullPointerException(); } public static void main(String[] args) { ExecutorService executorService = Executors.n

Java线程学习笔记(一)

一.线程的创建方式: 老掉牙的话题了,继承 java.lang.Thread父类或者实现Runnalbe接口,这里就提一句: class Thread implements Runnable Thread也是继承了Runnable接口的,Runnable才是大哥. 重写run(),run()里放的都是具体的业务,包括对线程的具体操作. class Thread1 implements Runnable { int i; Thread1(int i) { this.i = i; } @Overri

【转】linux 用户线程、LWP、内核线程学习笔记

[好文转发---linux 用户线程.LWP.内核线程学习笔记] 在现代操作系统中,进程支持多线程.进程是资源管理的最小单元:而线程是程序执行的最小单元.一个进程的组成实体可以分为两大部分:线程集合资源集.进程中的线程是动态的对象:代表了进程指令的执行.资源,包括地址空间.打开的文件.用户信息等等,由进程内的线程共享. 线程有自己的私有数据:程序计数器,栈空间以及寄存器. Why Thread?(传统单线程进程的缺点) 1. 现实中有很多需要并发处理的任务,如数据库的服务器端.网络服务器.大容量

线程学习笔记

1.什么是线程? 老师说过学好操作系统(Operating System)最重要的三个概念就是文件.虚存和进程了.之前已经学习过进程,因此对于线程的概念就比较好理解了. 进程是一个执行实体,操作系统是以进程为单位分配资源.在一个执行空间内可以用多个小型进程并发来完成不同的任务,这种小型的进程称之为线程.进程是一个比较大的概念,线程是一个比较具体化的小的概念,比如一个进程需要完成这样一个任务,读出用户收到的数据,将这些数据进行排序,再将这些数据输出,这是一个进程要完成的任务,这个进程中可以有三个线

线程学习笔记2

对于文章里面碰到的问题,可能也是我学习1当中出现的问题,新开的线程不管委托与否都在同一个线程里面,所以新线程运行的时候,主窗体也会停住,后来想在新线程中打开窗体的思路来处理,结果碰到下面文章的问题,在多方面的查找和学习后发现有一个办法可以解决它,希望对大家有帮助. private void button7_Click(object sender, EventArgs e) { Thread thread2 = new Thread(threadPro);//创建新线程 thread2.Start

线程学习笔记(二)

5.终止线程 线程退出的方式有3种 1.线程体函数执行结束,用 pthread_create() 函数创建一个新线程的时候会执行一个函数,这个函数就是线程体函数,如果该函数执行完毕,那么线程退出,类似于住进程的 main() 函数返回. 2.线程被另一个线程取消.这种方法类似于一个进程被另一个进程调用kill()函数杀死. 3.线程自行退出,类似于线程调用一个 exit() 函数. Linux系统中使用 pthread_exit(void *rval_ptr) 函数终止线程: 头文件:  #in

线程学习笔记(EventWaitHandler)AutoResetEvent的使用

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ConsoleApplication46 { class TwoWaySignaling { //事件等待句柄 static EventWaitHandle _ready = new AutoRese

线程学习笔记 等待句柄和线程池(摘自https://blog.gkarch.com/threading/part2.html#manualresetevent)

//如果你的应用有很多线程,这些线程大部分时间都在阻塞,那么可以通过调用ThreadPool.RegisterWaitForSingleObject来减少资源消耗.这个方法接受一个委托,它会在向等待句柄发信号时执行.当处于等待状态时,它不会浪费线程资源: static ManualResetEvent _starter = new ManualResetEvent (false); public static void Main() { RegisteredWaitHandle reg = Th

lua协程----ngx-lua线程学习笔记

--[[ - @desc lua数据输出 - @param string 字符串 - return string --]] function dump(v) if not __dump then function __dump(v, t, p) local k = p or ""; if type(v) ~= "table" then table.insert(t, k .. " : " .. tostring(v)); else for key

Java线程学习笔记(两) 线程异常处理

线程捕获异常: 情况下,我们在main()方法里是捕捉不到线程的异常的,比例如以下面代码: public class ExceptionThread implements Runnable{ @Override public void run() { throw new NullPointerException(); } public static void main(String[] args) { ExecutorService executorService = Executors.new