赞
踩
得分点 地址空间、开销、并发性、内存
标准回答:
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。
进程作为资源分配和调度的基本单位,频繁的对进程进行调度和切换,这种上下文切换的开销无疑是巨大的,而线程只是资源调度的基本单位,与进程相比上下文切换的开销减小了很多。如下图,创建、撤销进程以及上下文切换中开销较大。
进程PCB中有的信息:
线程TCB所需信息:
内核态与用户态:
进程可以并发执行,进程之间的线程也可以并发执行因此大大提高了并发性。
系统不会为线程分配内存资源,一个进程有多个线程,多个线程共享系统给进程提供的资源,线程本身指存在少量自身运行的一些必要资源。
为什么会有上下文的切换?
在任何一个时刻,单处理器系统都只能执行一个进程的代码。当操作系统决定要把控制权从当前进程转移到某个新进程时,就会进行上下文切换,即保存当前进程的上下文、恢复新进程的上下文,然后将控制权传递到新进程。新进程就会从它上次停止的地方开始。
上下文切换为什么要陷入内核?
假设现在有两个并发的进程:shel进程和hello进程。最开始,只有 shell进程在运行,即等待命令行上的输人。当我们让它运行hello程序时, shell通过调用一个专门的函数,即系统调用,来执行我们的请求,系统调用会将控制权传递给操作系统。操作系统保存 shell进程的上下文,创建一个新的hello进程及其上下文,然后将控制权传给新的hello进程。hello进程终止后,操作系统恢复shll进程的上下文,并将控制权传回给它, shell进程会继续等待下一个命令行输入。
从上面这个实例我们可以得出结论:
(1)上一个进程的上下文信息还在内存和处理器当中,我们要保存这些信息的话,就必须陷入到内核态才可以。
(2)创建一个新的进程,以及它的上下文信息,并且将控制权交给这个新进程,这些都只有在内核态才能实现。
综上,我们可以得出结论,进程和线程的上下文切换相较于协程比较“耗时耗力”。
那么协程的上下文切换相较线程有哪些提升?
协程上下文切换只涉及CPU上下文切换,而所谓的CPU上下文切换是指少量寄存器(PC / SP / DX)的值修改,协程切换非常简单,就是把当前协程的 CPU 寄存器状态保存起来,然后将需要切换进来的协程的 CPU 寄存器状态加载的 CPU 寄存器上就 ok 了。而对比线程的上下文切换则需要涉及模式切换(从用户态切换到内核态)、以及 16 个寄存器、PC、SP…等寄存器的刷新;
从多线程角度来讲:
很明显,CPU 调度切换的是进程和线程。尽管线程看起来很美好,但实际上多线程开发设计会变得更加复杂,要考虑很多同步竞争等问题,如锁、竞争冲突等。
多进程、多线程已经提高了系统的并发能力,但是在当今互联网高并发场景下,为每个任务都创建一个线程是不现实的,因为会消耗大量的内存 (进程虚拟内存会占用 4GB [32 位操作系统],而线程也要大约 4MB)。
大量的进程 / 线程出现了新的问题
系统线程会占用非常多的内存空间
过多的线程切换会占用大量的系统时间。
而协程刚好可以解决上述2个问题。协程运行在线程之上,当一个协程执行完成后,可以选择主动让出,让另一个协程运行在当前线程之上。并且,协程并没有增加线程数量,只是在线程的基础之上通过分时复用的方式运行多个协程,而且协程的切换在用户态完成,切换的代价比线程从用户态到内核态的代价小很多。
因此我们可以将线程分为 “内核态 “线程和” 用户态 “线程。
一个 “用户态线程” 必须要绑定一个 “内核态线程”,但是 CPU 并不知道有 “用户态线程” 的存在,它只知道它运行的是一个 “内核态线程”。
这样,我们再去细化去分类一下,内核线程依然叫 “线程 ”,用户线程叫 “协程”。
既然一个协程可以绑定一个线程,那么能不能多个协程绑定一个或者多个线程上呢。
于是,Go 为了提供更容易使用的并发方法,使用了 goroutine。goroutine 来自协程的概念,让一组可复用的函数运行在一组线程之上,即使有协程阻塞,该线程的其他协程也可以被 runtime 调度,转移到其他可运行的线程上。最关键的是,程序员看不到这些底层的细节,这就降低了编程的难度,提供了更容易的并发。
————————————————
版权声明:本文为CSDN博主「瘦弱的皮卡丘」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ThinPikachu/article/details/121325198
假设协程运行在线程之上,并且协程调用了一个阻塞IO操作,这时候会发生什么?实际上操作系统并不知道协程的存在,它只知道线程,因此在协程调用阻塞IO操作的时候,操作系统会让线程进入阻塞状态,当前的协程和其它绑定在该线程之上的协程都会陷入阻塞而得不到调度。
所以,在协程中尽量不要调用阻塞IO的方法,比如打印,读取文件,Socket接口等,除非改为异步调用的方式,并且协程只有在IO密集型的任务中才会发挥作用。
要牢记这句话:协程只有和异步IO结合起来才能发挥出最大的威力
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。