自学内容网 自学内容网

go 语言中协程和GMP模型

为什么需要协程?

协程用来更加精细地利用线程,支撑超高的并发的。协程,从 runtime 的角度看,协程就是一个被调度的 g 结构体。

G 就是协程,M 是线程,P 是为了优化多线程并发时,会抢夺协程队列的全局锁问题,很多资料称为 ”处理器“,它是介于 M 与 G 中介的。

会有多个 M 去全局队列获取 G 来执行,这个全局是个大锁,锁冲突比较严重。

P 作用登场:

  • M 与 G 之间的中介,处理器
  • P 持有一些 G,使得每次获取 G 的时候不用每次去全局队列获取,减少了并发冲突

如果本地 P 队列 和 全局队列也没有 G,这时候会有一个窃取可能,他会去其他线程持有的G,拿来执行。

新建的协程,优先将新的协程放入 P 的 runnext(插队),若 P 本地队列满了,放入全局队列。

基于系统调用和主动挂起,协程可能无法调度,以下解决方案

基于协作的抢占式调度:业务主动调用 morestack()

前面执行G的线程是个大任务,可能需要执行很久,导致其他协程饥饿。

基于信号的抢占式调度:强制线程调用 doSigPreempt()

假如一个函数执行某业务很长,永远不会调用 runtime.morestack() ,怎么办?

解决方案:基于信号的抢占式调度。就是操作系统中,有很多基于信号的底层通信方式如:SIGPIPE/SIGURG/SIGHUP 等,线程可以注册对应的信号的处理函数,可以实现马上跳转某些个方法执行其他业务了。

注册的信号处理函数(doSigPreempt),当垃圾回收器向循环线程发起 “抢占信号” 之后,陷入在执行业务方法的线程会立即跳到信号抢占方法(doSigPreempt()),做重新调度循环。

如果业务方法不调用 runtime.morestack(),可利用垃圾回收器的线程发送 SIGURG 信号,让这个业务方法强制跳到 信号处理函数 (doSigPreempt() ),重新回到 调度循环(Schedule),这样比较大的协程放在队列里面,优先调度饥饿的协程了。


原文地址:https://blog.csdn.net/realize_dream/article/details/144309682

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!