理解 Goroutine

在Go语言中,每一个并发的执行单元叫作一个Goroutine。Goroutine使我们在Go中创建并发执行非常方便。但同时也面临一些挑战,如:Goroutine 泄露 等问题。为了能用好Goroutine,我们需要了解一下它的基本原理。

在理解Goroutine之前,我们先复习一下进程和线程的概念。

进程和线程

进程是对运行时程序的封装,是系统进行资源调度和分配的的基本单位,实现了操作系统的并发

线程是进程的子任务,是CPU调度和分派的基本单位用于保证程序的实时性,实现进程内部的并发;线程是操作系统可识别的最小执行和调度单位

多个线程对应同一个进程,并基于进程的共享内存空间进行通讯,相较于进程的重量级,线程是比较小的(约2M),但频繁的线程调度也依然会消耗大量的内存,从而增加执行时间。

process.png

## Goroutine

Go 语言的调度器通过使用与 CPU 数量相等的线程减少线程频繁切换的内存开销,同时在每一个线程上执行额外开销更低的 Goroutine 来降低操作系统和硬件的负载。

goroutine.png

GMP模型

基本概念

  1. G — 表示 Goroutine,它是一个待执行的任务;
  2. M — 表示操作系统的线程,它由操作系统的调度器调度和管理;
  3. P — 表示处理器,它可以被看做运行在线程上的本地调度器;

gmp.png

执行过程:

  1. 全局队列存放等待运行的G
  2. P的本地队列,存放的也是等待运行的G 但是数量的大小是不超过256个 如果当前G中又创建G 那么会优先加入到当前P的本地队列。如果存不下了就会把本地的一半G放到全局队列
  3. P列表 所有P都在程序启动的时候创建,并保存在数组中。最多又GPMAXPROCS 个 可以在go程序中使用runtime.GOMAXPROCS(num) 配置
  4. M 线程想运行任务就得获取P 从P的本地队列获取G如果P队列为空 M也会尝试从全局队列拿一批G放到P的本地队列。或从其他P的队列中偷一半放到自己队列

参考

updatedupdated2021-11-242021-11-24