goroutine
一、介绍
goroutine 是 Go语言中的轻量级线程,用户态级别,由 Go 运行时(runtime)调度和管理,它比线程更加易用、高效和轻便
每一个并发的执行单元叫作一个goroutine,Go 程序会自动为 main() 函数创建一个默认的goroutine
1 goroutine和线程的区别
1) 内存消耗
创建一个goroutine的栈内存消耗为 2 KB,如果栈空间不够用,会自动进行扩容
创建一个线程需要消耗 1 MB 栈内存,而且还需要一个被称为 "a guard page" 的区域用于和其他线程的栈空间进行隔离
2) 创建和销毀
线程创建需要向操作系统申请资源,销毀时将资源归还,因此创建和销毀线程的资源消耗比较大,属于内核级
goroutine的创建和销毁是由 Go runtime 负责管理的,消耗非常小,属于用户级
3) 切换
线程的调度方式是抢占式的,切换时需要保存各种寄存器,以便将来恢复。线程切换会消耗 1000-1500 ns
goroutine的调度方式是协同式的,切换时只需保存三个寄存器: Program Counter, Stack Pointer and BP。goroutine 切换会消耗 200 ns
2 创建goroutine
使用 go 关键字创建一个goroutine,一个函数可以被创建多个goroutine,一个goroutine必定对应一个函数
每个groutine中的返回值都会被忽略,如果需要从goroutine中返回数据,请使用通道(channel)把数据从goroutine中返回
格式
go 函数名(参数列表)
二、Golang的GMP模型
1 GMP模型
G(Goroutine): go协程
M(Machine): 工作线程或内核线程,G 需要调度到 M 上才能运行
P(Processor): 调度器,负责调度goroutine,保存了当前goroutine运行的上下文(函数指针,堆栈地址及地址边界),同时还负责部分内存的管理。维护一个本地goroutine队列,M从P上获得goroutine并执行
其中:
M 与 P 的数量没有绝对关系
P在程序启动时创建,个数在程序启动时决定,由环境变量GOMAXPROCS的值或程序运行runtime.GOMAXPROCS()进行设置,默认情况下等同于CPU的核数
没有足够的M来关联P并运行其中的可运行的G时创建M
G需要在M上才能运行,M通过P的调度获取G,在某一时刻,一个M上只有一个G在运行(g0除外)
P拥有一个G队列,里面是已经就绪的G,是可以被调度到线程栈上执行的协程,称为运行队列
M堵塞,P会创建或者切换一个新的M
2 关系图
其中:
goroutine队列中: 黄色的G表示正在执行中的goroutine,灰色的G表示等待调度的goroutine
3 调度策略