在Go语言中,每一个并发的执行单元叫作一个Goroutine。Goroutine使我们在Go中创建并发执行非常方便。但同时也面临一些挑战,如:Goroutine 泄露 等问题。为了能用好Goroutine,我们需要了解一下它的基本原理。
在理解Goroutine之前,我们先复习一下进程和线程的概念。
进程和线程
进程是对运行时程序的封装,是系统进行资源调度和分配的的基本单位,实现了操作系统的并发;
线程是进程的子任务,是CPU调度和分派的基本单位,用于保证程序的实时性,实现进程内部的并发;线程是操作系统可识别的最小执行和调度单位。
多个线程对应同一个进程,并基于进程的共享内存空间进行通讯,相较于进程的重量级,线程是比较小的(约2M),但频繁的线程调度也依然会消耗大量的内存,从而增加执行时间。
## Goroutine
Go 语言的调度器通过使用与 CPU 数量相等的线程减少线程频繁切换的内存开销,同时在每一个线程上执行额外开销更低的 Goroutine 来降低操作系统和硬件的负载。
GMP模型
基本概念
- G — 表示 Goroutine,它是一个待执行的任务;
- M — 表示操作系统的线程,它由操作系统的调度器调度和管理;
- P — 表示处理器,它可以被看做运行在线程上的本地调度器;
执行过程:
- 全局队列存放等待运行的G
- P的本地队列,存放的也是等待运行的G 但是数量的大小是不超过256个 如果当前G中又创建G 那么会优先加入到当前P的本地队列。如果存不下了就会把本地的一半G放到全局队列
- P列表 所有P都在程序启动的时候创建,并保存在数组中。最多又GPMAXPROCS 个 可以在go程序中使用runtime.GOMAXPROCS(num) 配置
- M 线程想运行任务就得获取P 从P的本地队列获取G如果P队列为空 M也会尝试从全局队列拿一批G放到P的本地队列。或从其他P的队列中偷一半放到自己队列