-
并发/并行
从物理基础元素角度来看,当只有一个CPU时,执行一个程序这个程序就会一直占用CPU,直到程序运行结束。
如果这个程序的运行过程中,需要用到CPU的部分很快就结束了,程序的其他环节(比如IO阻塞等)正在占用时间,此时CPU是空置的。
于是就有了并发。
-
并发的不足
并发执行加速了对CPU的使用效率,也带来了问题。
程序A运行到一半,程序B进来抢占CPU,程序A的中间状态/内存/变量怎么办?
因此,在同一个CPU里的并发需要处理好上下文切换的问题。
-
进程的出现
让CPU处理两个程序,就像男女结婚一样。两人结婚并不是两个人凑在一起过日子,而是两个家庭在进程资产重组。不是丈夫+妻子,而是丈夫+妻子and丈夫+岳父and丈夫+岳母and妻子+婆婆…
将计算主业及所需附属等整合在一起抽象出来的概念,就叫进程。
这个概念能够独立运行。涉及到大量计算机资源的配置,如果任由用户程序配置,太过复杂也会冲突。
因此就出现了操作系统。
操作系统直接跟底层资源配置打交道,用户程序只需要跟操作系统打交道就行了。
-
上下文切换(Context Switch)
上下文切换主要是指进程的上下文切换,发生在内核态,由内核调度器执行。
上下文,指的是进程的运行状态。
当一个进程的时间片用完,内核将保存该进程的运行状态(即上下文),将其存入运行队列(Run Queue),同时让新的进程占用CPU。
进程的上下文包括:
- 内存地址空间
- 内核态堆栈
- 硬件上下文(CPU寄存器)
开销较大。
为了降低开销,出现了线程。
-
线程的出现
通过进程切换,充分利用CPU,然而进程切换过程中上下文切换是个高开销事件,就像两家人用共享同一栋房子,今天你拖家带口搬进去,明天他家锅碗瓢盆再搬一通,这个中间过程太费劲。
现在两家人各站一半房间,只对厨房进行轮换使用,这样开销小了,就是说上下文的包含范围减小了。
大量进程之间的切换过程会消耗很多系统资源,为了降低资源消耗,就出现了线程。
同一个进程中可以有不同的线程,这些线程可以共享一个地址空间、页表缓存区,每个线程只需要维护自己的寄存器、栈、相关变量。
最初是没有操作系统的,因为人工配置每个进程、线程的资源工程复杂又容易造成计算机崩溃,才有了操作系统(内核),操作系统嫁接在计算机硬件和程序代码之间,上面谈到的进程/线程切换,都是下沉到内核中由内核操作的,称为内核态。
为了进一步降低内核态线程上下文切换的开销,纤程出现了。
-
纤程(Fiber)
纤程(Fiber)是一种最轻量化的线程(lightweight threads)。它是一种用户态线程(user thread),让应用程序可以独立决定自己的线程要如何运作。操作系统内核不能看见它,也不会为它进行调度。
就像一般的线程,纤程有自己的定址空间。但是纤程采取合作式多任务(Cooperative multitasking),而线程采取先占式多任务(Pre-emptive multitasking)。应用程序可以在一个线程环境中创建多个纤程,然后手动运行它。纤程不会被自动运行,必须要由应用程序自己指定让它运行,或换到下一个纤程。
跟线程相比,纤程较不需要操作系统的支持。
一个线程内可以创建成千上万个纤程。
纤程用于化异步为同步 ,没有了线程所谓的安全问题, 避免锁机制。
简单理解:为了进一步减小内核态线程上下文切换的开销,于是有了用户态线程设计,即纤程。
-
用户态线程
如果连时钟阻塞、 线程切换这些功能我们都不需要了,自己在进程里面写一个逻辑流调度的东西。那么我们即可以利用到并发优势,又可以避免反复系统调用,还有进程切换造成的开销,分分钟给你上几千个逻辑流不费力。这就是用户态线程。
-
终于出现协程
如果一种实现使得每个线程需要自己通过调用某个方法,主动交出控制权。那么我们就称这种用户态线程是协作式的,即是协程。
协程的做法很像早期操作系统的协作式多任务。
协作式多任务:当任务得一个到了 CPU 时间,除非它自己放弃使用 CPU ,否则将完全霸占 CPU。
协程(coroutine)顾名思义就是“协作的例程”(co-operative routines)。跟具有操作系统概念的线程不一样,协程是在用户空间利用程序语言的语法语义就能实现逻辑上类似多任务的编程技巧。
协程的核心思想在于:控制流的主动让出和恢复。
这一点和上文说的用户态线程有几分相似,但是用户态线程多在语言层面实现,对于使用者还是不够开放,无法提供显示的调度方式。但是协程做到了这一点,用户可以在编码阶段通过类似
yieldto
原语对控制流进行调度。 -
References
单核CPU、并行、进程、线程、纤程、协程出现必要性解析
最新推荐文章于 2021-12-05 17:44:54 发布