1.1.线程概念(学习)
本章理解以下内容:
- 什么是多任务,以及多任务的不同类型
- 进程的概念
- 线程的概念
- 什么是主线程
- 什么是辅线程
- 多任务
多任务术语:操作系统一次性运行多个应用程序的能力;例如:在windows操作系统上打开了两个窗口Microsoft窗口和一个Microsoft outlook窗口。
另外,系统面板显示出系统后台还运行有其他应用程序。在这“应用程序”这个词不是很恰当,其实我们指的是线程。
传统上,多个任务有不同的风格。目前,Windows在线程中使用了一种风格。
a.在Windows系统早期版本(Windows 3.x)以及其他一些操作系统中,系统允许运行一个程序,直到该程序将占用的处理器资源释放给正在运行其他应用程序为止,从而实现协作。
因为这种方式是由应用程序与其他正在运行的程序协作,所以称为协作多式任务。
这种任务缺点是:如果一个程序不释放处理资源,其他程序就会被锁定。这像购买火车站里排队一样,出纳员一次只对一个顾客提供服务,在完成所有的事务处理之前,顾客不可能离开出纳窗口。
b.现在,使用Windows的当前版本就不会遇到这样的问题了,操作系统现在处理多任务的方法有很大的不同。
操作系统允许一个应用程序执行一段很短的时间,然后强制关闭它,让另一个应用程序执行。这种中断式多任务风格称为抢先式多任务机制。
抢先式多任务机制解决了处理器被锁定的问题,但同时带来了其他问题:
应用程序会共享诸如数据库连接和文件之类的资源。如果两个应用程序同时访问同一个资源会如何呢?
一个程序修改了数据,然后中断,让另一个程序也对这个数据进行修改。现在两个应用程序都修改了同一个数据。
可以使用同步技术可以解决抢先式多任务问题。
2.进程
当启动应用程序时,系统就会为该应用程序分配所需内存以及其他资源。
内存和资源的物理分离叫作进程。
当然应用程序可以启动多个进程。注意:“应用程序”和“进程”并不是同义词。分配给进程的内存与其他进程分配的内存被隔离,只有所属的那个进程才可以访问它。应用程序可能包含一个或多个进程。每个进程都有自己的独立的数据、执行代码和系统资源。
3.线程
在Windows操作系统启动任务管理器窗口中,包含了进程使用cpu的汇总信息,这是因为进程也有一个有计算机的处理器使用的执行次序。这个执行次序称为线程。
进程由寄存器定义,记录CPU的使用情况、线程使用的堆栈以及跟踪线程当前状态的容器。这个容器就叫作线程本地存储区。
3.1 单线程
每个进程至少有一个执行顺序或线程。创建一个进程包括在指令中的某一点启动线程。这个最初的线程称为基本线程或主线程。比如:Mian方法,winform 中application.Run()开始。
更准确的将,线程其实是指向进程的指令流部分的一个指针,线程实际是不包含指令,只是指出了当前和将来可能要使用的路径,而这是通过数据和分支判断确定指令来完成的。
3.2时间片
在讨论多任务时,操作系统为每个应用程序都授权了一个时间段,让应用程序在这个时间段中运行,之后中断该应用程序,让其他应用程序执行。
这样说其实是不太准确,实际上是处理器给进程授予时间。进程能够执行的时段称为时间片或时间量。程序员并不知道这个时间片,除操作系统之外,谁也无法预计时间片。所以程序员不应该把时间片看作一个常量。每个操作系统和处理器可能被分配了不同的时间片。
这时候就会产生一个问题,如果每个进程都进行物理分离,就应该考虑如何安排它们怎么执行。
3.3 多线程
可以将进程分解,以共享分配给它的时间片。通过在进程中产生额外的执行线程,就可以分解进程。可以产生一个额外的线程来完成后台工作,例如:访问网络或查询数据库。
这些辅线程常常用于完成某项工作,因此称为工作线程(Worker thread)。
这些线程将共享进程的内存空间。在进程产生的新线程就称为自由线程。
还有单元线程模型有很大的区别,在单元线程中,每个进程都拥有需要执行的全局数据的副本。而每个生成的线程都是在它本身的进程产生的,所以不能共享内存的数据。
而自由线程是将进程分解,以共享进程的内存空间,所以能共享内存的数据,但是处理器每次只执行一个线程,但是可以拥有多个cpu可供选择,.net还可以控制进程使用那个CPU。通过System.Diagnostics命名空间Prcoess类的PrcoessorAffinity属性,就可以完成这项工作。在同一个时间片有可能执行同一个进程中的线程,也可能执行另一个进程中的线程。
3.4 中断与线程本地存储的工作原理
当一个线程运行的时间超出了为它分配的时间片时,它不是停下来等待。每个处理器一次只能处理一个任务,所以当前的线程必须摆脱这种情况。但是,在当前线程再次跳出线程队列之前,必须存储状态信息,才能在下次执行时从本次中断的位置开始。这是线程本地存储器(Thread Local Storage, TLS)的一项功能。线程的TLS包括了寄存器、线程指针、调度信息、内存中的地址空间和其他资源的使用信息。存储在TLS中的一个寄存器是程序计数器,该计数器可以告诉线程接下来应该执行那条指令。
3.4.1 中断
中断是一种机制,并不是真正的中断一个线程,而是当线程发生中断时,Windows就使用一个特殊的函数,即中断处理程序将这个线程状态存储在 TLS中。对于中断之前存储的那个线程来书,当前的程序计数器也存储在TLS中。这个程序计数器仅仅是当前执行的指令的地址(注意:TLS实际并没有保存到队列中,而是保存在包含这个线程的进程的内存中,实际保存到队列中的是指向该内存的指针)。一旦线程的执行超时,它就会移到线程队列的末尾,并基于线程的优先级等候再次轮到自己执行。
3.4.2 线程睡眠与时钟中断
当线程中断后下一次再恢复执行时,资源又可能还是无法使用,甚至10或20次的执行中。这时候我们就会考虑线程是否退出执行的队列较长时间,这样,处理器就不必在从一个线程到另一个线程来回切换中浪费时间。线程自动退出队列执行一段时间,就称为睡眠,当线程进入睡眠时,它就会再次打包的到TLS中,不过这次,TLS并没有把该线程放到队列末尾,而是放到一个独立的睡眠队列中,为了睡眠队列上的线程再次执行,需要用另一种中断来标记它们,称为时间中断。
3.4.3 线程终止
在执行另一个线程的过程中,可以用一个请求显式的停止线程执行。当以这种方式结束线程时,称为终止。当线程结束时,线程的TLS就会释放其内存。但线程所使用的进程中的数据仍然存在,除非进程也结束。
3.4.4 线程的优先级
windows 将线程的优先级分为0-31级别,数字越大表示优先级越高。
只有系统设置为0级别,说明线程是空闲的。Windows 系统的用户可以设置1-15的优先级,而只有windows系统的管理员才能高于15优先级。
处理器将以优先级最高则最先执行的原则来调度所有线程,同一级别的将以线程循环的方式依次执行,优先级最高执行完之后,就执行仅次于其优先级的线程。