什么是goroutine
goroutine 是 Go 语言中的一种轻量级线程,由 Go 运行时(runtime)管理。
其优势是创建和销毁开销非常小并且是并发执行的,可以提高程序的执行效率。
其调度是Go运行时进行,无需手动管理。
不同的goroutine之间的通信是通过channel进行的。
特点是:相比线程,其启动的代价很小,以很小栈空间启动(2Kb左右)
能够动态地伸缩栈的大小,最大可以支持到Gb级别。与线程关系是n:m,即可以在n个系统线程上多工调度m个Goroutine
进程、线程、协程的区别
进程:是应用程序的启动实例,每个进程都有独立的内存空间,不同的进程通过进程间的通信方式来通信。
线程:从属于进程,每个进程至少包含一个线程,线程是 CPU 调度的基本单位,多个线程之间可以共享进程的资源并通过共享内存等线程间的通信方式来通信。
协程:为轻量级线程,与线程相比,协程不受操作系统的调度,协程的调度器由用户应用程序提供,协程调度器按照调度策略把协程调度到线程中运行
简单理解就是:进程依附于应用程序,线程依附于CPU调度,协程依附于用户
什么是GMP
- G 代表着 goroutine
- M(Machine)代表系统级线程。
- P(Processor) 代表逻辑处理器
每个P与多个G绑定:P有个局部队列,队列中储存着G。
每个P与一个M绑定,M是执行P中G的实体。
在数量级方面:
- G的数量理论上没有限制
- P的数量由runtime.GOMAXPROCS() 的,通常设置为逻辑cpu核心数的两倍
- M的数量由配置决定(默认是10000),可以通过、runtime/debug包中的SetMaxThreads设置。
GMP调度流程
图片参考自网站

GRQ (global runnable queue): 全局队列:存放所有正在等待运行的 G
LRQ (local runnable queue): 本地队列:每个 P 都有一个本地队列, 用于存放当前 P 等待和正在运行的 G,每个 P 的本地队列中最多存放 256 个 G 。创建 G 时,会优先放入本地队列,如果本地队列满了, 则会将队列中一半的 G 移动到全局队列中。
工作量窃取机制(work stealing)
当一个P的本地队列中没有G了,它将会优先从全局队列中窃取G。如果全局队列为空,它会从其他的P队列中窃取一半的G,放到自己的队列中。
移交机制(hand off)
M被阻塞时,P会被移交给其他空闲的M,若没有空闲的M,则会创建新的M来执行。
goroutine泄露
可能出现泄露的场景
- Goroutine内正在进行 channel/mutex 等读写操作,但由于逻辑问题,某些情况下会被一直阻塞。
- Goroutine内的业务逻辑进入死循环,资源一直无法释放。
- Goroutine内的业务逻辑进入长时间等待,有不断新增的 Goroutine 进入等待。
具体例子:
channel阻塞:某个channel发送了信息,但是并没有接受方。或者某个channel接收消息,但是并没有发送方。
channel未初始化,就进行读写。
不设置超时时间的等待:等待第三方接口传回消息,但是服务出现故障被阻塞。
资源锁设置错误:同步、互斥锁逻辑错误,出现阻塞。
避免方法:
- 超时控制,时间过长则关闭
- 通信处理,遇到异常发信号关闭。
1万+

被折叠的 条评论
为什么被折叠?



