线程概念简介 什么是线程 多线程上篇(七)

操作系统为了程序的并发执行引入了进程的概念,提高了资源的利用率以及吞吐量。

在20世纪 60年代人们提出了进程的概念后,在OS中一直都是以进程作为能拥有资源和独立运行的基本单位的。

直到 20 世纪 80 年代中期,人们又提出了比进程更小的能独立运行的基本单位——线程(Threads)

试图用它来提高系统内程序并发执行的程度,从而可进一步提高系统的吞吐量。

简言之,进程的概念,使之能够并发执行多道程序,线程的概念让你更好地并发执行程序,一个是能不能的问题,一个是更好的问题。

线程与进程对比

线程概念的发展

进程概念提出的目的就是为了多道程序并发执行,并发过程中必然意味着不断地进程调度任务切换,但是他又是资源分配的独立单位,也就是说他要背着资源来回跑。

举个例子:

办公室内,每个人都有一台电脑,电脑就是资源

然后大家经常需要不断地变换座位位置(比如大家都是哪里需要去哪里,客服缺人了,销售就顶一个过去)

每个人都抱着自己的电脑来回的换位置方便?还是大家只是人员走动,电脑就使用那个位置的电脑方便?

进程也是有些类似的道理,你带着这么多资源来回切换调度,必然会带来更多的时&&空开销

所以创建了线程的概念,程序运行时所需的资源和程序的调度进行解耦

进程仍旧负责资源的独立分配,但是线程作为调度运行的独立单位,仅仅携带自身运行的必备的一丁点资源。

对比

线程具有许多传统进程所具有的特征,所以又称为轻型进程(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)

互斥锁是一种比较简单的、用于实现线程间对资源互斥访问的机制

互斥锁可以有两种状态

  • 开锁(unlock)
  • 关锁(lock)

当一个线程需要读/写一个共享数据段时,需要对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);

简言之,借助于条件变量用于控制长时间的等待,锁用于控制对资源的同步。

总结

本文对线程进行了非常简单的介绍,线程之于进程在很多的方面有着极其类似的逻辑,尤其是从调度的视角看。

毕竟线程就是对进程中关于调度部分的独立抽象。

只要能够理解进程和线程的目的就能够很好地理解他们相似的原因,因为都是操作系统对于程序运行的抽象描述,线程是进程的更加细粒度的掌控。

在换句话说就是操作系统的角度对程序的执行抽象为:“资源的分配”“调度”

最初这两个概念都是加诸于进程这个概念上,后续为了更加高效将两个概念进行了拆分,就是这样

所以说,对于原先介绍的进程的相关概念中关于调度部分的绝大多数理论,都是适用于线程概念的

原文地址:线程概念简介 什么是线程 多线程上篇(七)

原文地址:https://www.cnblogs.com/noteless/p/10354612.html

时间: 2024-11-12 07:25:31

线程概念简介 什么是线程 多线程上篇(七)的相关文章

进程同步概念简介 多线程上篇(四)

进程同步概念 临界资源 一旦有对资源的共享,就必然涉及竞争限制 比如尽管有两个人去水井打水,但是水井却只有一个:合理安排的话刚好错开,但是如果安排不合理,那就会出现冲突,出现冲突怎么办?总有一个先来后到,等下就好了. 这个水井就是一个临界资源 临界资源用来表示一种公共资源或者说是共享数据,可以被多个线程使用. 但是每一次,只能有一个线程使用它,一旦临界资源被占用,其他线程要想使用这个资源,就必须等待. 当多进程访问临界资源时,比如打印机 假设A进程和B进程轮流获得CPU时间片执行,A打印数学,B

进程控制概念简介 多线程上篇(三)

进程控制 进程的基本数据信息是操作系统控制管理进程的数据集合,这些信息就是用来控制进程的,此处我们说的进程控制就是进程的管理. 比如进程有状态,那么进程的创建.终止,状态的切换,这都不是进程自主进行的,都是通过操作系统进行管理的 如下图所示,所有的相关数据都是操作系统用来管理维护进程的 操作系统抽象出进程概念的核心是为了运行程序 所以进程的执行态是最为核心的 其他的状态则是为了更好的控制管理进程以及进程的并发执行而附加的 所以,一定程度上来讲,操作系统对于进程的控制,可以认为是对于进程的不同状态

进程通信概念简介 多线程上篇(六)

进程通信指的是进程间的信息交换 ,IPC(Inter-Process Communication,进程间通信) 之前说到: 进程通信就相当于一种工作方式.沟通形式,比如你给我一个SVN标签号并且告知我意图,我去库中检索指定标签修改的指定内容,就完成了一个任务的协作. 进程同步中,也有信息的交换,比如互斥量访问,再比如生产者和消费者,共享缓冲池,但是这些通常被称之为低级的进程通信. 以信号量为例,如果你说我在塔顶点亮灯表示危险,否则表示安全,这没问题,如果你想用灯亮灯灭来像QQ一样,大段大段的传递

线程概念及多线程控制

多线程: 多进程:可以同时处理数据(并发/并行) 多线程:可以同时处理数据(并发/并行) 线程概念: 多进程任务处理(使用的是多个虚拟地址空间):将多个任务分解为多个程序(分解到多个进程中完成). 多线程任务处理:多个pcb 共用同一个虚拟地址空间,同时完成一个代码段中多个不同模块的功能. 进程的理解:只有一个线程的进程 线程的理解: 1.在传统操作系统中进程就是一个运行中程序中的描述信息 ——>pcb,控制程序的运行 2.linux中并没有为线程设计一个tcb 来控制线程的运行 3.在linu

JavaScript的多线程(工作线程)简介

首先声明,标题写成多线程主要目的是吸引眼球赚点击量,程序员一般看见多线程就会肾上腺素激升,你懂的... 其实更精确的定义是工作线程worker thread,本篇会详细为何起名叫工作线程worker thread,而非其他语言中的多线程multi thread,两者有些许差别. JavaScript原本是单线程的,一次只能处理一件事.如果处理数组占据大量时间,用户点击可能就没反应了,或页面刷新很慢,影响用户体验. HTML5之后可以新建一个工作线程来处理数组,确保之后的事情能顺利完成. 工作线程

进行概念详解 多线程上篇(二)

操作系统是程序与硬件交互的中间层,现代操作系统将程序的一次执行抽象为进程和线程的概念. 进程作为资源分配的基本单位,线程作为执行的基本单位. 进程和线程其实就是操作系统程序本身实现控制一个程序运行的数据项描述 所有的程序都是面向语言进行开发的,而语言本身是面向操作系统的,线程是操作系统对程序一次运行的抽象 所以,所有的多线程编程模型,必然遵从操作系统的大逻辑,必然是符合操作系统的对线程的抽象概念,操作系统在抽象之上提供了API供应用程序调用 简言之,应用程序的底层是借助于操作系统来完成多线程编程

Java多线程概念简介 多线程中篇(一)

Java的线程与操作系统的线程 在线程的相关介绍中,有讲到“线程的实现”分为三种:内核支持,用户级以及两者混合.(这只是一种简要的分类) Java线程在JDK1.2之前,是用户线程实现的 而在JDK1.2中,线程模型变为基于操作系统原生线程模型来实现的 所以说Java虚拟机中线程的映射实现,是受制于操作系统的,操作系统支持怎样的线程模型,决定了Java虚拟机中线程的样子. 虚拟机规范中也并未限定Java线程需要使用哪种线程模型来实现. 线程模型只对线程的并发规模和操作成本产生影响,对Java程序

Unix线程概念、控制原语、属性

线程: 线程基础概念: 线程在Linux中又称轻量级进程.并且它和进程都有PCB(进程控制块),但是区别是进程的虚拟地址空间是独享的,也就是每个进程都有自己的虚拟地址空间,但是线程的PCB是共享的,在同一个虚拟地址空间里面,每个线程有自己的PCB.虽然每个线程都有自己的PCB,但是从内核的角度来看,进程和线程是一样的,这是因为同一个虚拟地址空间里面的每个线程的PCB指向的内存资源的三级页表是相同的.在Linux下,可以把线程看做是最小的执行单位(进程内部运用多线程完成任务),而进程是最小的分配资

线程概念及线程的同步与互斥

线程概念:它是运行在进程内部的的一个基本执行流,多线程的控制流程可以长期并存,一个进程中的数据段和代码段都是被该进程中的多个线程共享的,若定义一个函数,每个线程都可以调用,若定义一个全局变量,每个线程都可以访问. 线程还共享进程的以下内容:1.文件描述符表 2.当前的工作目录 3.用户id(uid)和组id(gid) 4.每种信号的处理方式. 但每个线程还必须有自己的私有部分:1.线程id 2.硬件上下文(硬件寄存器的值,栈指针等) 3.自己的栈空间(运行时的临时数据都要保存在自己的栈空间上)