当我们在go func{}下有如何代码的时候,Go是如何处理阻塞的?
注:阻塞情况还有其它情况比如 加锁,网络连接读/写,select
我们需要知道在Go中代码的执行是需要线程(M)绑定 P 才能在CPU上执行
如果不知道什么是GMP,可以先阅读
【GMP】当我写下go func的时候,到底发生了什么?_程序员红豆的博客-CSDN博客我们先理解什么是G、M、PG∶goroutine,一个计算任务。由需要执行的代码和其上下文组成,上下文包括∶当前代码位置,栈顶、栈底地址,状态等。M∶ machine,系统线程,执行实体,想要在CPU上执行代码,必须有线程,与C语言中的线程相同,通过系统调用 clone 来创建。P:processor,虚拟处理器,M必须获得P才能执行代码,否则必须陷入休眠(后台监控线程除外),你也可以将其理解为一种 token,有这个 token,才有在物理 CPU核心上执行的权力。Go的调度流程本质上是一个生产-消https://blog.csdn.net/qq_37186127/article/details/125517300?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22125517300%22%2C%22source%22%3A%22qq_37186127%22%7D&ctrtid=DsOjK如果说我们线程在执行的时候阻塞了,那么程序是不是要无限创建线程才能执行?
答案在Go中,这种情况是不会阻塞调度的,而是会把goroutin挂起
所谓挂起,就是让G进入某个数据结构,待ready后再继续执行,不会占用线程
线程会进入schedule,继续消费队列,执行其它的G
什么是schedule?如下图(具体还是看的文章【GMP】当我写下go func的时候,到底发生了什么? )
各种阻塞处理情况上述图是Go中各种阻塞的情况下的处理
以channel举例,对应上图左上角的hchan
type hchan struct {
……
sendq waitq // list of send waiters
……
}
我们来看下在runtime中chan.go的hchan的数据结构
…… 代表省略了部分结构成员
sendq是一个waitq类型
type waitq struct {
first *sudog
last *sudog
}
first和last都是一个sudog类型指针
Go就是把这些阻塞的G或者sudog挂载到这些数据结构里面
我们之前说的都是G还有,那sudog是什么?他们之间有什么关系吗?
一个G可能会对应多个sudog,比如一个G会同时select多个channel
sysmon:system monitorsysmon是什么?我们先来了解一下Go里面的线程
我们在上述图可以看到线程的职责
gsignal执行信号处理时,切换到gsignal栈
G0执行runtime调度代码时,切换到m.g0(就是主线程,绿色的那个)
G 执行用户逻辑时的状态
而他们都是要绑定P才能执行的,而不需要P就可以执行
废话一大堆,那sysmon有什么用呢?
在这篇文章的sysmon之前所谈到的阻塞都是可接管的阻塞
那也有不可接管的阻塞,比如syscall(系统调用),长时间运行需要剥离P执行
比如程序中写了如下代码
GC时需要停止所有goroutine,而老版本的Go的g停止需要主动让出
什么是主动让出?GO协程(goroutine)的使用
如果不主动让出,那么GC时就hang死了,
那么在Go 1.14增加了基于信号的抢占之后,该问题被解决了
那这个信号的抢占就是由sysmon负责
上述图片是sysmon的职责
如果有G运行超过10ms,或者syscall阻塞超过10ms那么久会执行retake操作进行剥离
本文完~



