GO协程

goroutine

协程本意是coroutine,go协程是goroutine。
线程分为内核态线程(内核线程)和用户态线程(协程)。
线程由CPU调度是抢占式的,协程由用户态调度是协作式的(主动让出才执行下一个)。

N:1(N个用户线程绑定1个内核线程)

例子:python的gevent
优点:协程切换不陷入内核态,轻量快速。
缺点:无法充分利用CPU,协程阻塞后造成线程阻塞,其他协程都无法执行。

1:1(1个用户线程绑定1个内核线程)

例子:java.lang.Thread、C++11的std::thread
优点:充分利用CPU。
缺点:不同内核态线程之间上下文切换很慢。

M:N(M个用户线程绑定N个内核线程)

克服了以上2种映射关系的缺点。
go协程,goroutine没有父子关系,main函数是主goroutine。

不同于coroutine的2个地方

1. 如果协程阻塞造成线程阻塞,那么该线程的其他协程可以转移到其他可运行的线程上。

2. 一个goroutine最多占用CPU 10ms,防止其他goroutine饿死。

go函数执行时机

输出结果

Hello, Mark
Hello, Mark
Hello, Mark
Hello, Mark
Hello, Mark

解释

在for语句执行完毕之后才执行5个go函数,这时name的值是Mark。

改进1

改进2

GO调度器的组成

G、M和P的概念

G是goroutine。
M是物理处理器。
P是逻辑处理器,最多GOMAXPROCS(默认值是CPU核数,最大化并行)个。
M必须和一个P关联才能运行P拥有的G。

自顶向下GO调度器的4个部分

全局队列:存放等待运行的G。
P的本地队列:存放等待运行的G。新建G时,G优先加入到P的本地队列,如果队列满了,则会把本地队列中一半的G移动到全局队列。
M:关联P后,从P的本地队列获取G并运行。1. 如果P队列为空时,那么从其他P的本地队列拿一半放到P的本地队列。如果没有,那么从全局队列拿G放到P的本地队列。2. 因G执行系统调用造成线程阻塞时,M释放关联的P,把P转移给其他空闲的M执行。
GO调度器和OS调度器通过M结合起来,GO调度器绑定G和M,OS调度器绑定M和CPU。

参考资料

Go调度器系列(1)起源

Go调度器系列(2)宏观看调度器

 

posted on 2023-07-15 07:26  王景迁  阅读(48)  评论(0编辑  收藏  举报

导航