试图用它来提升系统内程序并发执行的程度,从而可进一步提升系统的吞吐量。
简言之,进程的概念,使之可以并发执行多道程序,线程的概念让你更好地并发执行程序,一个是能不能的问题,一个是更好的问题。
线程与进程对比
线程概念的发展
进程概念提出的目的就是为了多道程序并发执行,并发过程当中必然意味着不断地进程调度任务切换,可是他又是资源分配的独立单位,也就是说他要背着资源来回跑。
举个例子:
办公室内,每一个人都有一台电脑,电脑就是资源
而后你们常常须要不断地变换座位位置(好比你们都是哪里须要去哪里,客服缺人了,销售就顶一个过去)
每一个人都抱着本身的电脑来回的换位置方便?仍是你们只是人员走动,电脑就使用那个位置的电脑方便?
进程也是有些相似的道理,你带着这么多资源来回切换调度,必然会带来更多的时&&空开销。
因此建立了线程的概念,程序运行时所需的资源和程序的调度进行解耦
进程仍旧负责资源的独立分配,可是线程做为调度运行的独立单位,仅仅携带自身运行的必备的一丁点资源。
对比
线程具备许多传统进程所具备的特征,因此又称为轻型进程(Light-Weight Process)或进程元
相应地把传统进程称为重型进程(Heavy-Weight Process),传统进程至关于只有一个线程的任务。
在引入了线程的操做系统中,一般一个进程都拥有若干个线程,至少也有一个线程。
并发性
传统的OS系统,进程之间能够并发执行,引入线程概念的OS,不只仅进程间能够并发执行,一个进程中的线程也能够并发执行,不一样进程中的线程也能够并发执行
独立性
同一进程中的多个线程独立性比不一样进程间的独立性差不少。
每一个进程都是独立的地址空间和资源,同一进程下多线程他们共享进程下的资源,并且一般他们每每是用来相互合做的,每一个线程均可以访问所在进程的全部地址空间,好比一个线程打开的文件,能够被其余线程读写。
调度性
传统OS,进程做为资源分配和调度分派的基本单位,进程是能够独立运行的基本单位,不过进程调度切换时空开销大
引入线程的OS,线程是运行调度和分派的基本单位,线程才是独立运行的基本单位,线程切换时,仅仅须要保存和设置少许寄存器内容,代价远远小于进程切换,不过须要注意是同一个进程内线程切换不会进程切换,可是不一样进程中的线程进程切换,仍旧会致使进程切换。
拥有资源
进程拥有资源,而且做为系统中拥有资源的独立基本单位。
线程自身不拥有系统资源,仅仅拥有一点必不可少的,独立运行须要的资源,好比线程中的TCB。
除了自身的丁点儿资源外,共享所属进程的资源,同一个进程下全部线程,拥有相同的地址空间。
多处理器支持
传统进程(或者说单线程进程)只能运行于一个处理机上,无论有多少个处理机;
可是对于多线程进程,就能够将一个进程中的多个线程分配到多个处理机上,并行运行
简言之,多线程可让多核CPU充分发挥性能并行运行。
系统开销
进程和线程的建立撤销,系统都要为止分配和回收资源,好比内存空间、IO设备等,进程和线程的上下文切换,系统也都须要付出必定的时空开销。
可是,线程相关的开销明显小于进程。
线程简介
各线程之间也是存在资源共享和相互合做的,线程在运行时也是间断的,轮转切换的。
线程也是有运行状态的,这一点与进程并无本质区别,最主要的状态也是就绪、执行、阻塞
进程的控制核心信息保存在PCB中,线程也有对应的组成---TCB,全部用于控制和管理线程的信息都保存在TCB中
线程尽管是另一种彻底不一样的事物,可是毕竟是从进程的概念演化而来,也是操做系统对程序运行抽象的一部分,因此,线程必然与进程有着不少的类似点
线程实现
线程的实现主要有三种形式
从上面的分析中能够看得出来,内核支持和用户级都有各自明显的缺点和优势。
有些操做系统把用户级线程和内核支持线程两种方式进行组合,提供了组合方式ULT/KST 线程。
在组合方式线程系统中, 内核支持多KST线程的创建、调度和管理,同时,也容许用户应用程序创建、调度和管理用户级线程。
一些内核支持线程对应多个用户级线程,程序员可按应用须要和机器配置对内核支持线程数目进行调整,以达到较好的效果。
组合方式线程中,同一个进程内的多个线程能够同时在多处理器上并行执行,并且在阻塞一个线程时,并不须要将整个进程阻塞。
因此,组合方式多线程机制可以结合 KST和 ULT二者的优势,并克服了其各自的不足。
线程的同步与通讯
关于进程的同步与通讯的相关逻辑原理,对于进程的同步与通讯绝大多数都是适用的。
针对于这些原理,多线程OS也提供了多种同步机制,如互斥锁、条件变量、计数信号量以及多读、单写锁等。
信号量机制
进程中的信号量机制彻底适合多线程同步
根据用法分为两种
- 私用信号量(private samephore)
- 公用信号量(public semaphort)
系统运行中,有多个进程,进程中又有多个线程。
若是是为了同一进程中多个线程同步设置的信号量,量属于特定的进程全部,这就叫作私用,OS并不知道私用信号量的存在。
若是是为了避免同进程或者不一样进程中的线程之间而设置的,就叫作公用。其数据结构是存放在受保护的系统存储区中,由OS为它分配空间并进行管理,故也称为系统信号量。
互斥锁(mutex)
互斥锁是一种比较简单的、用于实现线程间对资源互斥访问的机制
当一个线程须要读/写一个共享数据段时,须要对mutex进行上锁,离开时须要解锁。
上锁时,首先校验 mutex 的状态,若是它已处于关锁状态,则试图访问该数据段的线程将被阻塞;若是 mutex处于开锁状态,则将 mutex 上锁后便去读/写该数据段。
线程完成操做后,必须将 mutex 解锁,同时还须要将阻塞在该互斥锁上的一个线程唤醒,其它的线程仍被阻塞在等待mutex打开的队列上。
另外,为了减小线程被阻塞的机会,在有的系统中还提供了一种用于 mutex 上的操做命令 Trylock。
顾名思义,并不会由于没法进入而阻塞,若 mutex 处于上锁状态,则 Trylock 并不会阻塞该线程,而只是返回一个指示操做失败的状态码。
条件变量
在许多状况下,只利用 mutex 来实现互斥访问可能会引发死锁,好比A线程请求资源顺序为R1,R2,B线程请求资源顺序为R2,R1
若是A对mutex 1上锁成功进入临界区后,须要获取R2的锁mutex 2,但是此时B得到了资源R2,对mutex 2已经上锁,此时,A等待mutex 2 B等待mutex 1,造成了死锁
因此说,锁,应该是仅仅用于在条件成立时进行操做时的一个同步保障,而不能在整个过程当中都依靠锁
能够借助于条件变量,就是条件
每个条件变量一般都与一个互斥锁一块儿使用,单纯的互斥锁用于短时间锁定,主要是用来保证对临界区的互斥进入。
而条件变量则用于线程的长期等待,直至所等待的资源成为可用的资源。
申请
Lock mutex
while (条件状态不知足) {
wait(condition variable);//释放锁,线程挂起等待,直到条件知足通知;
}
临界区其余操做
unlock mutex;
释放
Lock mutex
一些操做
unlock mutex;
wakeup(condition variable);
简言之,借助于条件变量用于控制长时间的等待,锁用于控制对资源的同步。
总结
本文对线程进行了很是简单的介绍,线程之于进程在不少的方面有着极其相似的逻辑,尤为是从调度的视角看。
毕竟线程就是对进程中关于调度部分的独立抽象。
只要可以理解进程和线程的目的就可以很好地理解他们类似的缘由,由于都是操做系统对于程序运行的抽象描述,线程是进程的更加细粒度的掌控。
在换句话说就是操做系统的角度对程序的执行抽象为:“资源的分配”“调度”
最初这两个概念都是加诸于进程这个概念上,后续为了更加高效将两个概念进行了拆分,就是这样
因此说,对于原先介绍的进程的相关概念中关于调度部分的绝大多数理论,都是适用于线程概念的