Golang 协程/线程/进程 区别详解
转载请注明来源:https://janrs.com/mffp
概念
进程 每个进程都有自己的独立内存空间,拥有自己独立的地址空间、独立的堆和栈,既不共享堆,亦不共享栈。一个程序至少有一个进程,一个进程至少有一个线程。进程切换只发生在内核态。
线程 线程拥有自己独立的栈和共享的堆,共享堆,不共享栈,是由操作系统调度,是操作系统调度(CPU调度)执行的最小单位。对于进程和线程,都是有内核进行调度,有 CPU 时间片的概念, 进行抢占式调度。内核由系统内核进行调度, 系统为了实现并发,会不断地切换线程执行, 由此会带来线程的上下文切换。
协程 协程线程一样共享堆,不共享栈,协程是由程序员在协程的代码中显示调度。协程(用户态线程)是对内核透明的, 也就是系统完全不知道有协程的存在, 完全由用户自己的程序进行调度。在栈大小分配方便,且每个协程占用的默认占用内存很小,只有 2kb ,而线程需要 8mb,相较于线程,因为协程是对内核透明的,所以栈空间大小可以按需增大减小。
并发 多线程程序在单核上运行
并行 多线程程序在多核上运行
协程与线程主要区别是它将不再被内核调度,而是交给了程序自己而线程是将自己交给内核调度,所以golang中就会有调度器的存在。
详解
进程
在计算机中,单个 CPU 架构下,每个 CPU 同时只能运行一个任务,也就是同时只能执行一个计算。如果一个进程跑着,就把唯一一个 CPU 给完全占住,显然是不合理的。而且很大概率上,CPU 被阻塞了,不是因为计算量大,而是因为网络阻塞。如果此时 CPU 一直被阻塞着,其他进程无法使用,那么计算机资源就是浪费了。
这就出现了多进程调用了。多进程就是指计算机系统可以同时执行多个进程,从一个进程到另外一个进程的转换是由操作系统内核管理的,一般是同时运行多个软件。
线程
有了多进程,为什么还要线程?原因如下:
- 进程间的信息难以共享数据,父子进程并未共享内存,需要通过进程间通信(IPC),在进程间进行信息交换,性能开销较大。
- 创建进程(一般是调用 fork 方法)的性能开销较大。
在一个进程内,可以设置多个执行单元,这个执行单元都运行在进程的上下文中,共享着同样的代码和全局数据,由于是在全局共享的,就不存在像进程间信息交换的性能损耗,所以性能和效率就更高了。这个运行在进程中的执行单元就是线程。
协程
官方的解释:链接:goroutines说明
Goroutines是使并发易于使用的一部分。 这个想法已经存在了一段时间,就是将独立执行的函数(协程)多路复用到一组线程上。 当协程阻塞时,例如通过调用阻塞系统调用,运行时会自动将同一操作系统线程上的其他协程移动到不同的可运行线程,这样它们就不会被阻塞。 程序员看不到这些,这就是重点。 我们称之为 goroutines 的结果可能非常便宜:除了堆栈的内存之外,它们的开销很小,只有几千字节。
为了使堆栈变小,
Go的运行时使用可调整大小的有界堆栈。 一个新创建的goroutine被赋予几千字节,这几乎总是足够的。 如果不是,运行时会自动增加(和缩小)用于存储堆栈的内存,从而允许许多goroutine存在于适度的内存中。 每个函数调用的CPU开销平均约为三个廉价指令。 在同一个地址空间中创建数十万个goroutine是很实际的。 如果goroutines只是线程,系统资源会以更少的数量耗尽。
从官方的解释中可以看到,协程是通过多路复用到一组线程上,所以本质上,协程就是轻量级的线程。但是必须要区分的一点是,协

本文详细介绍了Golang中协程、线程和进程的区别,强调了协程的高效性和轻量级特性。GMP调度器是Golang实现协程的关键,通过多路复用到一组线程上,实现协程间的高效调度。文章还探讨了GMP调度器的实现原理、设计策略和生命周期,展示了不同线程和协程的映射关系,以及调度流程。


最低0.47元/天 解锁文章
317

被折叠的 条评论
为什么被折叠?



