Goroutine MPG模型

·  阅读 97
Goroutine MPG模型
  • M:内核线程
  • P:处理器,用来执行 goroutine,它维护了本地可运行队列
  • G:goroutine,代码和数据结构
  • S:调度器,维护M和P的信息

设计历史

最开始Go只有M和G,还是单线程,即用线程调度自己的协程,利用全局锁去管理调度。

后面添加了多线程调度,充分利用了多CPU的并发优势。但是还是抢占式,只有一个锁和调度器。其实和我们常规的多线程开发没有什么区别。

终于,引入了P,增加了中间层。

P主要提供线程需要的上下文环境,负责调度线程上的等待队列,也就是管家的角色。

他会扫描自己底下的所有goroutine,根据信号来决定谁执行。

有意思的是,他自己名下没有需要执行的gorourtine,就会抢其他调度器的。

关于切换:

遇到系统调用的阻塞,保留上下文环境,让M执行其他G即可:

注意:网络 IO、channel 操作、锁这样的阻塞,线程并不会让出时间片。所以go还是会死锁的。

Goroutine 的状态

// defined constants
const (
	// G status
	//
	// Beyond indicating the general state of a G, the G status
	// acts like a lock on the goroutine's stack (and hence its
	// ability to execute user code).
	//
	// If you add to this list, add to the list
	// of "okay during garbage collection" status
	// in mgcmark.go too.
	//
	// TODO(austin): The _Gscan bit could be much lighter-weight.
	// For example, we could choose not to run _Gscanrunnable
	// goroutines found in the run queue, rather than CAS-looping
	// until they become _Grunnable. And transitions like
	// _Gscanwaiting -> _Gscanrunnable are actually okay because
	// they don't affect stack ownership.

	// _Gidle means this goroutine was just allocated and has not
	// yet been initialized.
	_Gidle = iota // 0
    //刚刚被分配并且还没有被初始化

	// _Grunnable means this goroutine is on a run queue. It is
	// not currently executing user code. The stack is not owned.
	_Grunnable // 1
    //没有执行代码,没有栈的所有权,存储在运行队列中

	// _Grunning means this goroutine may execute user code. The
	// stack is owned by this goroutine. It is not on a run queue.
	// It is assigned an M and a P (g.m and g.m.p are valid).
	_Grunning // 2
    //可以执行代码,拥有栈的所有权,被赋予了内核线程 M 和处理器 P

	// _Gsyscall means this goroutine is executing a system call.
	// It is not executing user code. The stack is owned by this
	// goroutine. It is not on a run queue. It is assigned an M.
	_Gsyscall // 3
    //正在执行系统调用,拥有栈的所有权,没有执行用户代码,被赋予了内核线程 M 但是不在运行队列上

	// _Gwaiting means this goroutine is blocked in the runtime.
	// It is not executing user code. It is not on a run queue,
	// but should be recorded somewhere (e.g., a channel wait
	// queue) so it can be ready()d when necessary. The stack is
	// not owned *except* that a channel operation may read or
	// write parts of the stack under the appropriate channel
	// lock. Otherwise, it is not safe to access the stack after a
	// goroutine enters _Gwaiting (e.g., it may get moved).
	_Gwaiting // 4
    //由于运行时而被阻塞,没有执行用户代码并且不在运行队列上,但是可能存在于 Channel 的等待队列上

	// _Gmoribund_unused is currently unused, but hardcoded in gdb
	// scripts.
	_Gmoribund_unused // 5
    //没有被使用,没有执行代码,可能有分配的栈

	// _Gdead means this goroutine is currently unused. It may be
	// just exited, on a free list, or just being initialized. It
	// is not executing user code. It may or may not have a stack
	// allocated. The G and its stack (if any) are owned by the M
	// that is exiting the G or that obtained the G from the free
	// list.
	_Gdead // 6
    //栈正在被拷贝,没有执行代码,不在运行队列上

	// _Genqueue_unused is currently unused.
	_Genqueue_unused // 7

	// _Gcopystack means this goroutine's stack is being moved. It
	// is not executing user code and is not on a run queue. The
	// stack is owned by the goroutine that put it in _Gcopystack.
	_Gcopystack // 8
    //栈正在被拷贝,没有执行代码,不在运行队列上

	// _Gpreempted means this goroutine stopped itself for a
	// suspendG preemption. It is like _Gwaiting, but nothing is
	// yet responsible for ready()ing it. Some suspendG must CAS
	// the status to _Gwaiting to take responsibility for
	// ready()ing this G.
	_Gpreempted // 9
    //由于抢占而被阻塞,没有执行用户代码并且不在运行队列上,等待唤醒

	// _Gscan combined with one of the above states other than
	// _Grunning indicates that GC is scanning the stack. The
	// goroutine is not executing user code and the stack is owned
	// by the goroutine that set the _Gscan bit.
	//
	// _Gscanrunning is different: it is used to briefly block
	// state transitions while GC signals the G to scan its own
	// stack. This is otherwise like _Grunning.
	//
	// atomicstatus&~Gscan gives the state the goroutine will
	// return to when the scan completes.
	_Gscan          = 0x1000
    //GC 正在扫描栈空间,没有执行代码,可以与其他状态同时存在
    
	_Gscanrunnable  = _Gscan + _Grunnable  // 0x1001
	_Gscanrunning   = _Gscan + _Grunning   // 0x1002
	_Gscansyscall   = _Gscan + _Gsyscall   // 0x1003
	_Gscanwaiting   = _Gscan + _Gwaiting   // 0x1004
	_Gscanpreempted = _Gscan + _Gpreempted // 0x1009
)
复制代码
  • 等待中:Goroutine 正在等待某些条件满足,例如:系统调用结束等,包括 _Gwaiting、_Gsyscall 和 _Gpreempted 几个状态;

  • 可运行:Goroutine 已经准备就绪,可以在线程运行,如果当前程序中有非常多的 Goroutine,每个 Goroutine 就可能会等待更多的时间,即 _Grunnable;

  • 运行中:Goroutine 正在某个线程上运行,即 _Grunning;

type m struct {
	g0   *g
	curg *g
	...
}
复制代码

其中 g0 是持有调度栈的 Goroutine,curg 是在当前线程上运行的用户 Goroutine,这也是操作系统线程唯一关心的两个 Goroutine。

欢迎来我的语雀空间:www.yuque.com/lixin-fjgsf…

分类:
后端
标签:
收藏成功!
已添加到「」, 点击更改