Go

Go协程介绍

2021-04-21  本文已影响0人  Dakini_Wind

参考自《go专家编程》
Go协程所实现的是M:N的线程模型,M个协程运行在N个线程中。

1. MPG模型

Go协程中有三个关键实体:

其关系如下图:

MPG模型图.png

关于M的数目:
M的个数是根据实际情况自行创建的,一般稍大于P的个数,为了保证runtime包的内置任务的运行。在运行中不够用时,也会再重新创建一个。
关于P的数目:
P的个数默认为CPU的核数,在IO密集的场景下可以适当提高P的个数。设置方式有两种,例:
设置环境变量:

export GOMAXPROCS=80

runtime.GOMAXPROCS()方法:

runtime.GOMAXPROCS(80)

全局队列由多个处理器共享,访问通过互斥锁来完成。
处理器P中的协程G额外再创建的协程会加入到本地的runqueues中。
两种情况下会放入全局队列中:1. 本地队列已满 2. 阻塞的协程被唤醒
全局队列会被处理器P周期性的摘取来调度。

2. 调度策略

每个处理器P维护着一个协程G的队列,处理器依次将协程G调度到M中执行。
此外,每个 P周期性的查看全局队列中是否有G将其调度到M中执行。
(全局队列中的G主要来自系统调用中恢复的G。)

前情提要:1.如一个线程进行系统调用会进入阻塞状态,一个协程进行系统调用也会导致线程进入阻塞状态。2. 前面提到M的数量会稍大于P的数量,多出来的M在系统调用时就会发挥作用。3. 当M不够用时,会再创建M

M0运行的G0产生系统调用时,M0将会释放P,进而某个冗余的M1将会获取P,继而执行P中所剩下的G。这时候M0进入阻塞,而M1接替M0的工作。

所以,在开发中一定要避免频繁的系统调用!!!一旦调用结束的速度跟不上调用产生的速度,那么系统资源将会有严重的浪费!

G0的系统调用结束后呢?
此时根据M0是否能获取到P来对G0作出不同的处理:1. 如果有空闲的P,则获取继续执行G0。2. 如果没有空闲的P,将把G0放入全局队列,等待被其他的P调度。M0将进入缓存池。

工作量窃取是为了实现P的负载均衡。
当当前P没有协程调度,并且全局队列中也没有需要调度的协程时,会从另一个正在运行的处理器P中偷取协程,每次偷取一半。

该机制是为了避免某个协程长时间执行,而阻碍到其他协程被调度。
调度器监控每个协程的执行时间,当其执行时间过长且有其他协程在等待是,会把协程暂停,转而调度等待的协程,达到类似时间片轮转的效果。

上一篇 下一篇

猜你喜欢

热点阅读