本文介绍进程、线程、协程的概念。
预备知识
程序的定义:由若干条具有一定功能的指令所组成的解题顺序和步骤。
早期的计算机只能跑单道程序,同一时间段内,各种资源(CPU,内存,硬盘,I/O)被当前跑的程序占用。
后期计算机引入了多道程序,同一时刻,有多个程序并发的运行。
问题:内存中同时放入多道程序,各个程序的代码、运算数据存放的位置不同。操作系统要怎么才能找到各程序的存放位置呢?
解决:为了方便操作系统管理,完成各程序并发执行,引入了进程、进程实体的概念。系统为每个运行的程序配置一个数据结构,称为集成控制(PCB),用来描述集成的各种信息(如程序代码存放的位置),PCB、程序段、数据段三部分构成了进程实体(进程映像)。
进程的定义:
进程是程序的一次执行过程。
进程是一个程序及其数据在处理机上顺序执行时所发生的活动。
进程是具有独立功能的程序在数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。
进程的组成:
进程的组成 [01]
PCB介绍:
PCB介绍
进程的特征:
进程的特征
Python创建进程:
进程与程序区别
线程的定义:
现代操作系统在运行一个程序时,会为其创建一个进程。例如,启动一个JAVA程序,操作系统就会创建一个Java进程。现代操作系统调度的最小单元是线程,也叫轻量级进程(Light Weight Process),在一个进程里可以创建多个线程,这些线程都拥有各自的计数器、堆栈和局部变量等属性,并且能够访问共享的内存变量。处理器在这些线程上高速切换,让使用者感觉到这些线程在同时执行。
一个Java程序从main()方法开始执行,然后按照既定的代码逻辑执行,看似没有其他线程参与,但实际上Java程序天生就是多线程程序,因为执行main()方法的是一个名称为main的线程。
为什么要使用多线程?
执行一个简单的“Hello, World!”,却启动了那么多的“无关”线程,是不是把简单的问题复杂化了?当然不是,因为正确使用多线程,总是能够给开发人员带来显著的好处,而使用多线程的原因主要有以下几点。
(1)更多的处理器核心
随着处理器上的核心数量越来越多,以及超线程技术的广泛运用,现在大多数计算机都比以往更加擅长并行计算,而处理器性能的提升方式,也从更高的主频向更多的核心发展。如何利用好处理器上的多个核心也成了现在的主要问题。
线程是大多数操作系统调度的基本单元,一个程序作为一个进程来运行,程序运行过程中能够创建多个线程,而一个线程在一个时刻只能运行在一个处理器核心上。试想一下,一个单线程程序在运行时只能使用一个处理器核心,那么再多的处理器核心加入也无法显著提升该程序的执行效率。相反,如果该程序使用多线程技术,将计算逻辑分配到多个处理器核心上,就会显著减少程序的处理时间,并且随着更多处理器核心的加入而变得更有效率。
(2)更快的响应时间
有时我们会编写一些较为复杂的代码(这里的复杂不是说复杂的算法,而是复杂的业务逻辑),例如,一笔订单的创建,它包括插入订单数据、生成订单快照、发送邮件通知卖家和记录货品销售数量等。用户从单击“订购”按钮开始,就要等待这些操作全部完成才能看到订购成功的结果。但是这么多业务操作,如何能够让其更快地完成呢?
在上面的场景中,可以使用多线程技术,即将数据一致性不强的操作派发给其他线程处理(也可以使用消息队列),如生成订单快照、发送邮件等。这样做的好处是响应用户请求的线程能够尽可能快地处理完成,缩短了响应时间,提升了用户体验。
多线程的缺点:
(1)等候使用共享资源时造成程序的运行速度变慢。这些共享资源主要是独占性的资源 ,如打印机等。
(2)对线程进行管理要求额外的 CPU开销。线程的使用会给系统带来上下文切换的额外负担。当这种负担超过一定程度时,多线程的特点主要表现在其缺点上,比如用独立的线程来更新数组内每个元素。
(3)线程的死锁。即较长时间的等待或资源竞争以及死锁等多线程症状。
多进程和多线程的主要区别?
线程是进程的子集(部分),一个进程可能由多个线程组成。多进程的数据是分开的、共享复杂,需要用IPC;但同步简单。多线程共享进程数据,共享简单;但同步复杂。
什么是多进程?
进程是程序在计算机上的一次执行活动,即正在运行中的应用程序,通常称为进程。当你运行一个程序,你就启动了一个进程。每个进程都有自己独立的地址空间(内存空间),每当用户启动一个进程时,操作系统就会为该进程分配一个独立的内存空间,让应用程序在这个独立的内存空间中运行。
在同一个时间里,同一个计算机系统中如果允许两个或两个以上的进程处于运行状态,这便是多进程,也称多任务。现代的操作系统几乎都是多任务操作系统,能够同时管理多个进程的运行。
多任务带来的好处是明显的,比如你可以边听mp3边上网,与此同时甚至可以将下载的文档打印出来,而这些任务之间丝毫不会相互干扰。
什么是多线程?
线程是一个轻量级的子进程,是最小的处理单元;是一个单独的执行路径。可以说:线程是进程的子集(部分),一个进程可能由多个线程组成。
线程是独立的。如果在一个线程中发生异常,则不会影响其他线程。它使用共享内存区域。
多线程是一种执行模型,它允许多个线程存在于进程的上下文中,以便它们独立执行但共享其进程资源。
多进程和多线程的区别:
多进程和多线程的区别
大量的进程/线程出现了新的问题?
好了,然后工程师们就发现,其实一个线程分为“内核态“线程和”用户态“线程。
(1)用户级线程
用户级线程(User Level Thread,ULT)是在用户(目态)空间实现的,是建立在用户进程内的线程,由对应的应用程序通过存放在用户空间的一组管理线程的过程(即线程库)完成对线程的管理工作。ULT的特点为:ULT与内核无关,因此,同一进程中的ULT的调度和切换简单而快速;虽然内核不知道用户级线程的存在,但却管理着该线程所属进程的活动,如果该用户级线程调用了系统调用而处于运行态,那么所属进程还是该阻塞时要阻塞,即线程状态与进程状态是独立的;如果内核阻塞了一个进程,则该进程中所有的线程都将被阻塞;内核只将处理器分配给进程,因此每次只有一个进程中的一个线程在一个处理器上运行。
(2)内核级线程
内核级线程(Kernel Level Thread,KLT)是在核心空间实现的,通过系统调用,由操作系统创建,是依赖于系统内核的线程。由操作系统内核完成对线程的管理工作。线程是CPU调度的基本单位,操作系统为每个线程保持一个核心栈。KLT可以在用户进程中,也可以在系统进程中。
KLT的特点:同一进程中的KTL切换需要进入核心模式下才能完成;当进程中的一个线程被阻塞时,进程中的另一个线程可以被调度;内核可以调度进程中的多个线程,使其同时在多个CPU上并行运行;内核本身也都可以使用多线程方式编写,从而改善操作系统本身的并行性。
除了内核级和用户级线程之外,还可以采用将两者混合的实现机制。即线程的实现分为两个层次:用户层和核心层。内核必须支持内核级线程的创建、调度和管理,同时也允许应用程序创建、调度和管理用户级线程。线程的创建、调度、同步通常在用户空间完成,某个应用程序的多个ULT能分配和对应于一个或多个KLT,KLT可同时并行运行于多个CPU,且在阻塞一个ULT时,内核可调度另一个线程执行。
线程模型
线程模型就是指操作系统内线程的实现策略,一般有一对一、多对一和多对多3种模型。
(1)一对一模型
将每个ULT映射到一个KLT,它能够保障并发性,但是对能支持的线程总数有一定的限制。
(2)多对一模型
将多个ULT映射到一个KLT。线程管理在用户空间中进行,效率较高。虽然它能创建很多ULT,但内核一次只能调度一个线程执行,因此,并发性得不到保障。
(3)多对多模型
将多个ULT映射到同样数量或较少数量的KLT。它很好地解决了上面出现的问题,能够创建需要的线程,并且内核也能并行执行这些线程。
协程的定义
协程,又称微线程,纤程。英文名Coroutine,是一种用户态的轻量级线程。
程序,或者称为函数,在所有语言中都是层级调用,比如A调用B,B在执行过程中又调用了C,C执行完毕返回,B执行完毕返回,最后是A执行完毕。
所以子程序调用是通过栈实现的,一个线程就是执行一个子程序。子程序调用总是一个入口,一次返回,调用顺序是明确的。
而协程的调用和子程序不同。
线程是系统级别的它们由操作系统调度,而协程则是程序级别的由程序根据需要自己调度。
在一个线程中会有很多函数,我们把这些函数称为子程序,在子程序执行过程中可以中断去执行别的子程序,而别的子程序也可以中断回来继续执行之前的子程序,这个过程就称为协程。
也就是说在同一线程内一段代码在执行过程中会中断然后跳转执行别的代码,接着在之前中断的地方继续开始执行。
协程的优点:
(1)无需线程上下文切换的开销,协程避免了无意义的调度,由此可以提高性能(但也因此,程序员必须自己承担调度的责任,同时,协程也失去了标准线程使用多CPU的能力);
(2)无需原子操作锁定及同步的开销;
(3)方便切换控制流,简化编程模型;
(4)高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。
协程的缺点:
(1)无法利用多核资源:协程的本质是个单线程,它不能同时将单个CPU的多个核用上,协程需要和进程配合才能运行在多CPU上。当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。
(2)进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序。