内容简介:程序启动时 创建n个操作系统线程 每个线程上面都会运行调度函数;由于 Go 语言是协作式的调度,不会像线程那样,在时间片用完后,由 CPU 中断任务强行将其调度走。对于 Go 语言中运行时间过长的 goroutine,Go scheduler 有一个后台线程在持续监控,一旦发现 goroutine 运行超过 10 ms,会设置 goroutine 的“抢占标志位”,之后调度器会处理。但是设置标志位的时机只有在函数“序言”部分,对于没有函数调用的就没有办法了。由于 Go 1.14 前都无法抢占正在执行无限循环
程序启动时 创建n个操作系统线程 每个线程上面都会运行调度函数;
由于 Go 语言是协作式的调度,不会像线程那样,在时间片用完后,由 CPU 中断任务强行将其调度走。对于 Go 语言中运行时间过长的 goroutine,Go scheduler 有一个后台线程在持续监控,一旦发现 goroutine 运行超过 10 ms,会设置 goroutine 的“抢占标志位”,之后调度器会处理。但是设置标志位的时机只有在函数“序言”部分,对于没有函数调用的就没有办法了。
由于 Go 1.14 前都无法抢占正在执行无限循环且没有任何函数调用的 goroutine,因此一旦出现死循环,将要进行 GC 的时候,其他所有 goroutine 都会停止,并且都在等着无限循环的 goroutine 停下来,遗憾的是,由于 for{} 循环里没有进行函数调用,无法插入抢占标记并进行抢占。
因运行时间过长与因系统调用时间过长而导致的抢占是有差别的:
对于运行时间过长的goroutine,系统监控线程首先会提出抢占请求,然后工作线程在适当的时候会去响应这个请求并暂停被抢占goroutine的运行,最后工作线程再调用schedule函数继续去调度其它goroutine;
而对于系统调用执行时间过长的goroutine,调度器并没有暂停其执行,只是剥夺了正在执行系统调用的工作线程所绑定的p,要等到工作线程从系统调用返回之后绑定p失败的情况下该goroutine才会真正被暂停运行。
由于运行时间过长导致的抢占调度,可以看到go的抢占调度机制并非无条件的抢占。需要抢占时,监控线程负责给被抢占的goroutine设置抢占标记,被抢占的goroutine再在函数的的入口处检查g的stackguard0成员决定是否需要调用morestack_noctxt函数,从而最终调用到newstack函数处理抢占请求。
当M0返回时,它会尝试从其他线程中“偷”一个上下文过来,如果没有偷到,会把Goroutine放到Global runqueue中去,然后把自己放入线程缓存中。 上下文会定时检查Global runqueue。
上一讲讲完了 main goroutine 的诞生,它不是第一个,算上 g0,它要算第二个了。不过,我们要考虑的就是这个 goroutine,它会真正执行用户代码。
g0 栈用于执行调度器的代码,执行完之后,要跳转到执行用户代码的地方,如何跳转?这中间涉及到栈和寄存器的切换。
开始main goroutine 1首先创建创建监控线程,该线程独立于调度器,不需要跟 p 关联即可运行 2创建监控线程,该线程独立于调度器,不需要跟 p 关联即可运行 3开启垃圾回收器 4main 包的初始化,递归的调用我们 import 进来的包的初始化函数 5调用 main.main 函数 6进入系统调用,退出进程,可以看出 main goroutine 并未返回,而是直接进入系统调用退出进程了exit(0)
欢迎关注我们的微信公众号,每天学习Go知识
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 理解golang调度之一 :操作系统调度
- 理解golang调度之二 :Go调度器
- Golang 源码学习调度逻辑(三):工作线程的执行流程与调度循环
- Node.js CPU调度优化(多服务器多核心分配调度)
- Hadoop 容器调度器与公平调度器原理和实践深入剖析-Hadoop商业环境实战
- [译] Kubernetes 调度详解
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。