并发概念计算机.docx
《并发概念计算机.docx》由会员分享,可在线阅读,更多相关《并发概念计算机.docx(38页珍藏版)》请在课桌文档上搜索。
1、并发概念(计算机)一.串行程序、并发程序、并行程序串行程序:指只能被顺序执行的指令列表。并发程序:并发程序属于程序,其内部是由多个部分(串行程序)组合在一起,这些部分组合在一起构成了一个整体,叫做并发程序。也叫单元并行程序:指可以在并行的硬件上(CPU,服务器),运行的并发程序。二、并发系统、并行系统并发系统:程序和程序之间是通过协议协商一致后形成通信。并发系统就是多个并发程序间构成的,看作是一个系统。并行系统:指并发系统以并行的方式存在。每个并发系统中的程序,很有可能会部署在多个并行硬件上(服务器)同时运行,也叫做分布式系统。三、并发程序内部交互1 .什么是并发程序的内部交互?并发程序中存在
2、多个串行程序,这些串行程序之间可能会存在数据交互的需求。比如多个串行程序对一个共享资源进行访问(数据库,消息队列);又或者在它们之间传递一些数据。在这些需求的环境下,协调它们的执行,就要涉及到同步。2 .同步的作用避免在并发访问共享资源时发生的冲突,可以有条不紊的传递数据。3 .同步的原则当程序要访问一个共享资源时,就必须请求该共享资源并获取对应的访问权;反之如果程序不再需要一个共享资源,就要放弃该资源的访问权。(释放资源,Close()而应该等到其释放资源后在进行访问(锁)。结论:一个共享资源在同一时刻内,只能被一个程序占用。同步会导致系统资源的浪费,耦合性太强。所以有了异步通信的思想。4
3、.异步通信发送方可以不加延迟的将消息发送出去,接收方也不会造成发送方的等待。(解耦)数据会被临时存储在一个通道缓存(IPC)的通道中。通道缓存是一种特殊的共享资源,可以被多个程序使用。接收方准备就绪后无需知道发送方,直接从通道缓存中获取数据。多线程编程概念(计算机)一、什么是多线程编程POSIX:可移植性的UniX操作系统接口。NPTL:LinUX操作系统的最新线程库。多线程编程是一种比多进程编程更灵活,更高效的编程方法。在Linux系统中提供了一个以POSIX为标准定义的线程(简称:POSIX线程)为中心的各种系统调用。POSIX也是go并发编程在Iinux系统下真正使用的内核接口,也就是N
4、PTL本地POSIX线程库。二、什么是线程线程可以视为进程中的控制流,一个进程中至少要包含一个线程。因为一个进程中至少要一个控制流持续运行。所以第一个线程会随着其进程的启动而创建,这个线程也被称之为主线程。一个进程中可以存在多个线程,这些线程是由当前进程中存在的线程创建出来的,而创建的方法就是系统调用。更准确的说是调用pthread_create函数。拥有多个线程的进程可以并发执行多个任务,并且即使某个线程被阻塞也不会影响到进程中其它任务的执行。线程会大大提升程序的响应效率和吞吐量线程不能独立于进程之上,线程的生命周期不能超过其所在进程的生命周期。三、如何实现一个线程通过系统调用来控制线程。线
5、程与进程的父子家族关系树结构不同,线程之间的关系都是平等的。它们之间不存在层级关系,任何线程都可以对同一进程中的其它线程进行有效的管理。其中管理分为5种:pthread_create创建线程pthread_cancel终止线程pthread_join连接已经终止线程pthread_detach分离线程pthread_exit彻底退出主线程3-1V线程TID标识和进程一样,线程也有属于自己的ID,叫做TIDo但与进程不同的是,线程ID在系统内中并不唯一,只会在其当前进程下唯一。不过Iinux系统则确保了每个线程在系统内ID的唯一性。目当某个线程不复存在后,其TID可以被其它线程复用。线程ID由操
6、作系统内核进行分配和维护。四、线程运行时发生异常会怎么样线程异常后会强制调用pthread,cancel函数来终止线程,然后调用detach函数分离线程。根据不同的异常情况会进入僵死状态和终止状态两种。僵死状态就是还会保留一些程序所需的资源,不会删掉线程上的全部数据。如果不再需要僵死线程,就会调用PthreadJoin终止掉僵死线程。五、如何在两个线程间共享数据一个进程中的所有线程都拥有自己的线程栈,并以此来存储线程私有的数据栈。这些线程的线程栈包含在其所属进程的虚拟内存空间当中。一个进程中的很多资源都会被其所有线程共享,这些被线程共享资源包括(在当前进程的虚拟内存中存储的):数据段、代码段、
7、堆、栈、信号,以及当前进程支持的文件描述符。正因如此,同一个进程中的多个线程运行的肯定是一个程序。只不过具体的流控方式会有所不同。另外,创建一个线程也不会像创建进程那样费劲,因为线程需要的所有代码、数据段、资源都在其进程中存储不需要被复制就能使用。操作系统内核提供了若干系统调用以便应用程序能够管理当前进程中的所有线程,还可以通过相应的系统功能协调这些线程的运行。六、线程状态线程状态分为:1 .就绪状态2 .运行状态3 .阻塞状态4 .睡眠状态当调用pthread_create函数时线程会进入就绪状态。当线程获得运行时机被CPU激活运行之后会进入运行状态。在线程运行中如果出现了阻塞等待,就会进入
8、睡眠状态,阻塞解除后会重新进入就绪状态。在线程运行中如果出现了return语句,或者退出线程的语句,根据当时不同的线程执行环境会进入僵尸状态和终止状态。不管是僵尸状态还是终止状态最终都会被回收。七、线程调度在线程的生命周期中,操作系统内核对线程的调用是非常核心的部分。正是因为有了调度器的实时调度和切换,才给众多线程一种并行运行的幻觉。调度器会把事件划分成极小的时间片,并把这些时间片分配给不同的线程,以使众多线程都有机会在CPU上运行。一个线程什么时候能够获得CPU时间,以及在CPU上运行多久,都是调度器的工作范畴。调度器:最大程度保证多核CPU之间的平衡运行。7-1.什么是线程调度,什么是线程
9、切换线程调度(也称线程间的上下文切换),线程的执行总是趋向于CPU受限或者I/O受限。也就是说线程的调度只分为两类:一些线程需要花费一定的时间使用CPU进行计算,另一些线程会花费一些时间等待相对较慢的I/O操作完成。调度器会依据它对线程的趋向性的猜测把他们进行分类,并让I/O受限的线程具有更高的动态优先级以及优先使用CPUo调度器会认为I/O操作往往会花费更长的时间,所以应该让它们尽早执行。这也是为了让众多线程运行的更加高效。在人决定下一个要敲击的按键、磁盘在磁道中定位簇或者网卡从网络中接收数据帧的时候,CPU可以腾出手来位其它线程服务。7-2.线程的静态、动态优先级调用线程的动态优先级是可以
10、被调度器实时调整的,而与之相对应的线程的静态优先级只能由程序指定。如果应用程序没有指定一个线程的静态优先级,那么默认为Oo调度器不会改变线程的静态优先级。线程的动态优先级就是调度器在其静态优先级的基础上调整得出的,动态优先级决定了线程的运行顺序。而线程的静态优先级决定了线程单次在CPU上运行的最长时间,也就是调度器分配给它的时间片的大小。所有等待使用CPU的线程会按照动态优先级从高到低的进行顺序排序,并依序放到与该CPU对应的运行队列当中。因此,下一个运行的线程总是动态优先级最高的那一个。7-3V线程的优先级队列每一个CPU的运行队列中都包含两个优先级阵列。其中一个用于存放正在等待运行的线程,
11、暂时称之为(激活的优先级阵列)。另一个则用于存放正在等待的运行的线程,暂时称为(过期的优先级阵歹I)。下一个运行的线程总是会从激活的优先级队列中选出。如果CPU发现某个线程占用了CPU很久的时间,并且激活的优先级队列中还有优先级与他相同的线程在等待运行,那么调度器就会让那个等待的线程在CPU上运行,而被换下的则进入过期的优先级阵列。当激活的优先级阵列中没有等待运行的线程时,调度器会把这两个优先级阵列的身份互换,之前的激活阵列变成过期阵列,现在的过期阵列变成激活阵列。如此,放入过期的优先级阵列的线程就又有机会运行了。当然,线程并不是总会处于运行和就绪状态。他还有可能会进入阻塞睡眠状态,处于睡眠状
12、态的线程是不能够被调度和运行的。它们会从两个阵列中移除。七、线程模型线程的实现模型分为三个,分别是:用户级线程模型、内核级线程模型、两级线程模型。它们之间最大的差异就在于线程与内核实体(内核调度实体KES)之间的对应关系。内核调度实体就是被内核调度器所调用的实体对象。也被称为内核级线程,是操作系统得最小运行单元。1 .用户级线程模型(M:1):此模型下的线程是由用户级别的线程库全权管理的。线程库并不是内核的一部分,而只是存储在进程的用户空间之中,这些线程的存在对于内核而言是无法感知的。用户级线程并不是内核的调度器的调度对象。对线程的各种管理和协调完全是用户及程序的自主行为,与内核无关。应用程序
13、在对线程进行创建、终止、切换或同步等操作的时候,并不需要让CPU从用户态转变成内核态。2 .内核级线程模型Q:1):该模型下的线程是由内核负责管理的,它门是内核的一部分。应用程序对线程的创建、终止和同步都必须通过内核提供的系统调用来完成(PthreaCL*)。进程中的每一个线程都会与一个KES(内核调度实体)相对应。也就是说,内核可以分别为每一个线程进行调度。由此,内核级线程模型也被称为Ll的线程实现。一对一线程实现消除了多对一线程实现的很多弊端。可以真正的实现线程的并发运行。3 .两级线程模型(M:N):两级线程模型的目标是取前两种模型的精华,去其糟粕,也成为多对多的(M:N)线程实现。与其
14、他模型相比,两级线程模型提供了更多的灵活性。在此模型下一个进程可以与多个KSE(内核调度实体)相关联,这与内核级线程模型相似,不同点在于进程中的线程并不与KES对应,这些应用程序线程可以映射到同一个已经关联的KES上。实现了两级线程模型的线程库,会通过操作系统内核创建多个内核级线程。然后它会通过这些内核级线程对应用程序线程进行调度。大多数此类线程库都可以将这些应用程序线程动态地与内核级线程关联。这样的设计显然使线程的管理工作更加复杂,因为这需要线程库与内核级线程共同努力和协作才能正确、有效的运行。八、线程同步线程同步的目的就是为了更好地协同工作或者维持数据的一致性。1 .共享数据的一致性一个进
15、程所拥有的相当一部分虚拟内存地址都可以被进程中所有线程共享,所以这些共享数据大多是以内存的空间作为载体。如果两个线程同时读取同一块共享内存但获取到的数据却不相同,那么程序很有可能就会出现某种错误。这是因为,共享数据的一致性往往代表着某种约定,而只有在该约定成立的前提下,多线程程序中的各个线程才能够使相应的流程执行正确。换句话说,如果操作的共享数据的结果,总是与约定的结果相同,就说明共享数据的一致性得到了保证。实际上,保证共享数据一致性的最简单最彻底的方法就是使该数据成为一个不变量。例如:常量就是不变量,它不可能被改变,也就不可能出现不一致的情况。因此无论当前程序中有多少个可能访问常量的线程,都
16、不需要采取任何措施。但是程序中不可能都是常量,我们需要通过额外的手段保证被多个线程共享的变量的一致性,这样就有了临界区的概念。2 .临界区临界区是只能被串行化访问或执行的某个资源或者代码,也被称为串行区域。保证临界区有效的最佳方式就是利用同步机制。在针对多线程程序的同步机制中包含了很多同步方法,包括原子性操作和互斥量,以及条件变量。3 .互斥量在同一时刻,只允许一个线程处于临界区之内的约束,称之为互斥(mutex)o每个线程在进入临界区之前,都必须先锁定某个对象,只有成功锁定对象的线程才会允许进入临界区,否则就会阻塞,这种对象被称为互斥对象或互斥量。互斥量有两种状态,既锁定状态、未锁定状态。互
17、斥量每次只能锁定一次,处于已锁定状态的互斥量不能被再次锁定。除非它已经解锁,否则任何!线程都不能对它进行二次加锁。如果对一个已锁定的互斥量进行加锁操作,那么这个操作必定会失败。成功锁定互斥量的线程会成为该互斥量的所有者,只有互斥量的所有者才能对其进行解锁。从这个角度讲,多个线程对同一个互斥量的争相锁定也可以看作是对互斥量的释放。当线程离开临界区的时候,必须要对相应的互斥量进行解锁。这样其它想进入该临界区而被阻塞的线程才会被唤醒,并且有机会再次尝试锁定该互斥量。在这些线程中只有一个线程会成功锁定该互斥量。注意:对同一个互斥量的锁定与解锁应该成队出现(defer)。4 .线程安全性,怎么实现线程安
18、全性?如果有一个代码块,它可以被多个线程并发执行,且总能够产生预期的结果,那么该代码块就是线程安全的。如果代码块对共享数据进行了更新操作,那么这个代码块就是非线程安全的。但是如果该代码块处于临界区中,那么这个代码块就是线程安全的。但是,为了实现线程安全,把所有的代码都置于临界区中虽然可行,但也是一种最低效的方法。可以仔细从函数体中查找出操作共享数据的代码并用互斥量将它们保护起来。还可以将这写代码从函数体中分离出来,然后再将它们聚集在一起成为一个函数或者一个结构体。为这个函数或结构体实现线程安全性。GMP一、早期的GMP调度器1 .当内核线程获取go的协程时,会经过调度器访问go的全局协程队列。
19、go的全局协程队列是由锁来进行保护的。当拿到队列的锁之后,会执行队列中的首个goroutine协程,其余的goroutine依次向前挪动一位。2 .内核执行完goroutine协程任务之后,会释放锁,并把这个goroutine还回全局协程队列,只不过这时候会把goroutine放在队尾。早期调度器的弊端:1.创建、销毁、调度G都需要每个内核线程M获取锁,形成了激烈的锁竞争。2 .内核M转移goroutine,会造成延迟和额外的系统负载。3 .系统调用(CPU在多个M之间切换)导致频繁的线程阻塞和取消阻塞操作,增加了系统开销。二、GMP模型简介GMP是三种元素的缩写。M与P是相互引用的关系,P与
20、G是一对多的调用关系。G:goroutine协程,go中的代码片段,协程程序。P:ProCeSSOr处理器,一个P代表执行了一个G。代码片段中所必需的资源(上下文环境)M:Machine,一个M代表一个内核线程,或者称为工作线程。简单来说,一个协程(G)的执行需要P(处理器)和M(内核)的支持,一个M在与P关联之后,就形成了一个有效的协程(G)运行环境(内核线程+上下文环境)。每个P都会包含一个可运行的协程(G)队列(runq)。该队列中的协程(G)会被依次传递给本地的P关联M,并获得运行。2-1V详解M1 .什么情况下会建立一个M一个M代表一个内核线程。大多数情况下,创建M都是由于没有足够的
21、M来关联P,并运行其中可运行的Go在系统执行系统监控和垃圾回收等任务的时候,也会创建M。M是一个结构体。2 .M与P和G是如何关联的M是一个结构体,这个结构体中包含了关联P,G的字段。typemstructgo*gcurg*gPpuintptr当前与M关联的Pnetppuintptr潜在关联的Pspinningbool表示M是否正在寻找可以运行的G。在寻找过程中M会处于自旋状态。Go在运行时可以讲一个M和一个G锁在一起,一旦锁定,这个M就只能运行这个G。Iockedg *g表示与M锁定的G3 .M是什么时候被创建的如何创建的?在M被创建之后,GO系统会先对它进行一番初始化,其中包括对自身所持有
22、的栈空间以及信号处理方面的初始化。M在创建之初,M会被加入全局的M列表中。这时它的起始函数和预联的P也会被设置。当运行时,系统会为这个M专门创建一个新的内核线程并与之关联。如果M就做好了执行G的准备。起始函数只有当M执行系统监控和垃圾回收任务的时候才会被设置。全局M队列在运行时系统需要的时候,会通过全局M队列,获取到所有M的信息,同时防止M被当成垃圾回收掉。4 .空闲M,M停止会如何?运行中的M有时也会被停止,比如在执行垃圾回收任务的过程中。运行时系统在停止M的时候,会把它放入调度器的空闲M列表。这很重要,因为在需要一个未被使用的M时,调度器会先尝试从空间的M中获取。M是否空闲,仅以它是否存在
23、于调度器的空闲M队列表中为依据。5 .能否设置M的最大数量?单个G。程序所使用的M的最大数量是可以设置的,G。程序运行的时候会先启动一个引导程序,这个引导程序会为其建立必要的环境。在初始化调度器的时候,它会对M的最大数量进行初始设置,这个初始值是IooO0,也就是说一个Go程序最多可以使用IOooO个M。2-2V详解P1.什么是PP是能够在M中运行的关键。GO的调度器会适时地让P与不同的M建立或者断开关联,以使P中那些可运行的G能够及时获得运行时机,这与操作系统内核在CPU之上实时的切换不同进程或线程的情形类似。P的最大数量实际上是对程序中并发运行的G的规模的一种限制。P的数量即为可运行G的队
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 并发 概念 计算机

链接地址:https://www.desk33.com/p-947623.html