进程、线程、协程与Goroutine

进程与线程(隔离与并行)

进程

进程可看作为分配资源的基本单位。比如你new出了一块内存,就是操作系统将一块物理内存映射到你的进程地址空间上(进程创建必须分配一个完整的独立地址空间),这块内存就属于这个进程,进程内的所有线程都可以访问这块内存,其他进程就访问不了,其他类型的资源也是同理。所以进程是分配资源的基本单位,也是我们说的隔离。

线程

线程作为独立运行和独立调度的基本单位,进而我们可以认为线程是进程的一个执行流,独立执行它自己的程序代码。线程上下文一般只包含CPU上下文及其他的线程管理信息,线程创建的开销主要取决于为线程堆栈的建立而分配内存的开销,这些开销并不大。线程还分为系统级别和用户级线程,用户级别线程对引起阻塞的系统调用的调用会立即阻塞该线程所属的整个进程,而内核实现线程则会导致线程上下文切换的开销跟进程一样大,所以经常的折衷的方法是轻量级进程(Lightweight)。在 Linux 中,一个线程组基本上就是实现了多线程应用的一组轻量级进程。线程的作用就在于充分使用硬件CPU,也就是我们说的并行。

协程

协程的核心机制

在实模式编程下,理论上操作系统只能加载一个进程,那个时候进程要使用系统服务的方法很简单,就是手工产生一个中断,就会触发CPU的中断处理机制,会保护好发起中断的现场,会将当前执行地址设置为对应的中断处理函数的地址,处理完以后回到刚刚保存的现场。其实这个过程,本质上就是协程的核心流程了。

为何使用中断

基于中断的方式,发起方和处理方可以使用自己的context,系统通过中断的方法来达到提供系统服务的目的,一个很重要的原因就是可以保障在很多情况下,都能让系统处理函数至少能有一个可用的context(属于系统的资源),这样当用户进程的context资源耗尽的情况下,也能调用一些系统服务。

协程与线程

  1. 协程与线程主要区别是:协程将不再被内核调度,而是交给了程序自己;而线程是将自己交给内核调度。所以也不难理解golang中调度器的存在。
  2. 从我们应用角度来说,我们一般将协程理解为用户态轻量级线程,是对内核透明的,也就是系统并不知道有协程的存在,是完全由用户的程序自己调度的,因为是由用户程序自己控制,那么就很难像抢占式调度那样做到强制的CPU控制权切换到其他进程/线程,通常只能进行协作式调度,需要协程自己主动把控制权转让出去之后,其他协程才能被执行到。

协程与Goruntine

  1. 协程是Coroutine,Go语言的协程是Goruntine,两者是不一样的。
  2. Golang语言作者Rob Pike也说,“Goroutine是一个与其他goroutines 并发运行在同一地址空间的Go函数或方法。一个运行的程序由一个或更多个goroutine组成。它与线程、协程、进程等不同。它是一个goroutine“。
  3. Go 协程通过通道来通信,而协程通过让出和恢复操作来通信。
  4. Go 协程比协程更强大。因为Golang 在 runtime、系统调用等多方面对 goroutine 调度进行了封装和处理,也就是Golang 有自己的调度器,工作方式基本上是协作式,而不是抢占式,但也不是完全的协作式调度,例如在系统调用的函数入口处会有抢占。当遇到长时间执行或者进行系统调用时,会主动把当前 goroutine 的CPU ( P) 转让出去,让其他 goroutine 能被调度并执行,也就是我们为什么说 Golang 从语言层面支持了协程。简单的说就是Golang自己实现了协程并叫做Goruntine
  5. Goruntine的优势在于并行和非常低的资源使用,体现在内存消耗方面和切换(调度)开销方面,每个 goroutine (协程) 默认占用内存远比 Java 、C 的线程少,只有2KB,而线程则需要8MB;线程切换涉及模式切换(从用户态切换到内核态)、16个寄存器、PC、SP…等寄存器的刷新等;而goroutine 只有三个寄存器的值修改:PC / SP / DX。

GOMAXPROCS

官方解释: GOMAXPROCS sets the maximum number of CPUs that can be executing simultaneously。 很清楚,就是限制CPU数,限制CPU数,本质上是什么,就是限制并行数,并行数即同时执行数量,执行单元即线程,即限制最大并行线程数量。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章