进程、线程、协程
1、对比
进程、线程、协程都是计算机中用于实现多任务的概念,它们之间有如下的区别和对比:
- 进程(Process)是计算机中资源分配和调度的基本单位,每个进程都有独立的内存空间,相互之间互不干扰,具有独立的执行流程和系统资源。**进程之间通信需要借助于进程间通信(IPC)的机制,如管道、信号、消息队列、共享内存等。**切换开销大。
- **线程(Thread)是进程的子任务,是操作系统调度的基本单位,每个线程都共享所属进程的内存空间,**彼此之间可以共享内存和资源。**线程之间通信需要借助于同步原语(如锁、信号量等)来进行协调,以防止线程之间的竞争和冲突。**切换开销小。
- **协程(Coroutine)是一种轻量级的线程,也称为用户级线程,不需要操作系统的调度,而是由程序本身控制调度。**协程具有比线程更小的上下文切换开销和更高的并发性能,因为在协程中,切换是由程序自身控制的,而不需要进行系统调用。**协程之间通信也可以使用同步原语来进行协调。**切换开销最小。
2、进程
在程序启动时,操作系统会给该程序分配一块内存空间,对于程序但看到的是一整块连续的内存空间,称为虚拟内存空间,落实到操作系统内核则是一块一块的内存碎片的东西。为的是节省内核空间,方便对内存管理。
![img](https://i-blog.csdnimg.cn/blog_migrate/b1d8753890af26a8de9af0dbcded5148.jpeg)
就这片内存空间,又划分为用户空间与内核空间,**用户空间只用于用户程序的执行,若要执行各种IO操作,就会通过系统调用等进入内核空间进行操作。**每个进程都有自己的PID,可以通过ps命令查看某个进程的pid。
3、线程
线程是进程的一个执行单元,一个进程可以包含多个线程,只有拥有了线程的进程才会被CPU执行,所以一个进程最少拥有一个主线程。
由于多个线程可以共享同一个进程的内存空间,线程的创建不需要额外的虚拟内存空间,线程之间的切换也就少了如进程切换的切换页表,切换虚拟地址空间此类的巨大开销。
至于进程切换为什么较大,简单理解是因为进程切换要保存的现场太多如寄存器,栈,代码段,执行位置等,而线程切换只需要上下文切换,保存线程执行的上下文即可。线程的的切换只需要保存线程的执行现场(程序计数器等状态)保存在该线程的栈里,CPU把栈指针,指令寄存器的值指向下一个线程。相比之下线程更加轻量级。
可以说进程面向的主要内容是内存分配管理,而线程主要面向的CPU调度。
4、协程
虽然线程比进程要轻量级,但是每个线程依然占有1M左右的空间,在高并发场景下非常吃机器内存,比如构建一个http服务器,如果一个每来一次请求分配一个线程,请求数暴增容易OOM(out of memery),而且线程切换的开销也是不可忽视的。同时,线程的创建与销毁同样是比较大的系统开销,因为是由内核来做的,解决方法也有,可以通过线程池或协程来解决。
协程是用户态的线程,比线程更加的轻量级,操作系统对其没有感知,之所以没有感知是由于协程处于线程的用户栈能感知的范围,是由用户创建的而非操作系统。
Golang里的goroutine是对协程的抽象,相比于其他n-1的协程,一个协程阻塞可能会造成后续协程的阻塞,而Go中的goroutine与线程是N-M的关系,可以通过hand-off机制实现切换。
这里的G为协程,P为调度器。
如一个进程可拥有以有多个线程一样,一个线程也可以拥有多个协程。协程之于线程如同线程之于cpu,拥有自己的协程队列,每个协程拥有自己的栈空间,在协程切换时候只需要保存协程的上下文,开销要比内核态的线程切换要小很多。
栈空间,在协程切换时候只需要保存协程的上下文,开销要比内核态的线程切换要小很多。**