【Go并发编程】第一篇 - Goroutines调度

进程和线程

当运行一个应用程序的时候,操做系统会给这个应用程序启动一个进程。咱们能够将进程看做一个包含应用程序在运行中须要用到和维护的各类资源的容器。一个进程至少包含一个线程,这个线程就是主线程。操做系统会调度线程到不一样的CPU上执行,这个CPU不必定就是进程所在的CPU。 golang

  • 进程:资源的全部权
  • 线程:执行和调度的基本单位
  • 同一进程下的各个线程共享资源,但寄存器、栈、PC不共享

Go调度

基本术语

Go Runtime管理调度,垃圾收集和Goroutine的运行时环境。这里咱们只谈调度器。算法

Runtime调度器经过把Goroutine绑定到操做系统线程来运行它们。Goroutine能够看做是轻量级的线程。每一个Goroutine用G来表示,它包含了用来跟踪栈的字段和当前状态。并发

Runtime跟踪每个G而且把它们绑定到P(Logical Processor)。P能够被看做抽象资源或者上下文,操做系统线程用M来表示(OS Thread)须要获取它以便来执行G。你能够经过runtime.GOMAXPROCS(numLogicalProcessors)来调整P(Logical Processors)。操作系统

G-P-M调度模型

Go 1.1中实现了G-P-M调度模型和work stealing算法,这个模型一直沿用至今: 线程

每个G(Goroutine)须要绑定到P(Logical Processor)才能调度执行;就像每个User-level Thread绑定到Kernel Thread才能被调度执行。code

M、P 和 G 之间的交互

建立一个Goroutine并准备运行,这个Goroutine会被放到调度器的Global队列中。而后调度器就将这些队列中的Goroutine分配给一个P(Logical Processor),并放到这个P对应的Local队列中。Local队列中的Goroutine会一直等待直到本身被分配的P执行。cdn

若是正在运行的Goroutine被一个系统调用阻塞,如打开一个文件。当这种状况发生时,M2(OS Thread)和Goroutine会从P0(Logical Processor)上分离,这个M2会一直阻塞直到系统调用返回(见上图右边)。与此同时,这个P0就失去了用来运行的M2。因此,调度器会建立一个新的M3,并将其绑定到该P0上。以后,调度器会从Local队列中选择另一个Goroutine运行。一旦刚才阻塞的系统调用执行完毕并返回,对应的Goroutine会放回到Local队列。blog

并发(Concurrency)不是并行(Parallelism)

上图解释了并发和并行的区别 并发 - 一个咖啡机同时服务两个队列 并行 - 两个咖啡机服务同时服务两个队列队列

若是但愿让Goroutine并行,必须使用多于一个P(Logical Processor)。当有多个P时,调度器会将Goroutine平等分配到每一个P上。这会让Goroutine在不一样M(OS Thread)上运行。不过要想真的实现并行的效果,用户须要让本身的程序运行在有多个物理处理器的机器上。进程

参考资料。

  1. Go In Action
  2. 也谈goroutine调度器
  3. Go scheduler: Ms, Ps & Gs
相关文章
相关标签/搜索