go语言调度之timer

在上一篇说了timer是如何创建出来,已经对应的源码,可以看出来这个是绑定在了当前的goroutine上的P的times,并且在未来的指定时间去执行。

之前看过gopark的源码都知道,在gopark最后,对调用park方法,然后会最后调用schedule方法,也就是重新寻找可以执行的goroutine。其实schedule的方法最重要是寻找可以执行的goroutine,也就是findRunnable方法。

然后看一下findRunnable的方法。

// Finds a runnable goroutine to execute.
// Tries to steal from other P's, get g from local or global queue, poll network.
// tryWakeP indicates that the returned goroutine is not normal (GC worker, trace
// reader) so the caller should try to wake a P.
func findRunnable() (gp *g, inheritTime, tryWakeP bool) {_g_ := getg()// The conditions here and in handoffp must agree: if// findrunnable would return a G to run, handoffp must start// an M.top:_p_ := _g_.m.p.ptr()if sched.gcwaiting != 0 {gcstopm()goto top}if _p_.runSafePointFn != 0 {runSafePointFn()}// now and pollUntil are saved for work stealing later,// which may steal timers. It's important that between now// and then, nothing blocks, so these numbers remain mostly// relevant.now, pollUntil, _ := checkTimers(_p_, 0)........

可以看出这里的checkTimers就是执行timer的地方

// checkTimers runs any timers for the P that are ready.
// If now is not 0 it is the current time.
// It returns the passed time or the current time if now was passed as 0.
// and the time when the next timer should run or 0 if there is no next timer,
// and reports whether it ran any timers.
// If the time when the next timer should run is not 0,
// it is always larger than the returned time.
// We pass now in and out to avoid extra calls of nanotime.
//
//go:yeswritebarrierrec
func checkTimers(pp *p, now int64) (rnow, pollUntil int64, ran bool) {// If it's not yet time for the first timer, or the first adjusted// timer, then there is nothing to do.next := int64(atomic.Load64(&pp.timer0When))nextAdj := int64(atomic.Load64(&pp.timerModifiedEarliest))if next == 0 || (nextAdj != 0 && nextAdj < next) {next = nextAdj}// 判断是否有需要执行的timerif next == 0 {// No timers to run or adjust.return now, 0, false}// 传入的now是0,那么就使用当前的if now == 0 {now = nanotime()}// 判断是否有需要执行的timerif now < next {// Next timer is not ready to run, but keep going// if we would clear deleted timers.// This corresponds to the condition below where// we decide whether to call clearDeletedTimers.if pp != getg().m.p.ptr() || int(atomic.Load(&pp.deletedTimers)) <= int(atomic.Load(&pp.numTimers)/4) {return now, next, false}}// 加锁lock(&pp.timersLock)// 加锁后再判断一次if len(pp.timers) > 0 {// 根据当前的时间再次调整P的timers 调整timers中的顺序 因为存在reset重置的情况adjusttimers(pp, now)for len(pp.timers) > 0 {// Note that runtimer may temporarily unlock// pp.timersLock.if tw := runtimer(pp, now); tw != 0 {if tw > 0 {pollUntil = tw}break}ran = true}}// If this is the local P, and there are a lot of deleted timers,// clear them out. We only do this for the local P to reduce// lock contention on timersLock.if pp == getg().m.p.ptr() && int(atomic.Load(&pp.deletedTimers)) > len(pp.timers)/4 {clearDeletedTimers(pp)}unlock(&pp.timersLock)return now, pollUntil, ran
}

这里runtimer核心,接下来看一下runtimer的实现

// runtimer examines the first timer in timers. If it is ready based on now,
// it runs the timer and removes or updates it.
// Returns 0 if it ran a timer, -1 if there are no more timers, or the time
// when the first timer should run.
// The caller must have locked the timers for pp.
// If a timer is run, this will temporarily unlock the timers.
//
//go:systemstack
func runtimer(pp *p, now int64) int64 {for {t := pp.timers[0]if t.pp.ptr() != pp {throw("runtimer: bad p")}// 判断当前状态switch s := atomic.Load(&t.status); s {case timerWaiting:// 是否到执行的时间if t.when > now {// Not ready to run.return t.when}// 切换为运行状态if !atomic.Cas(&t.status, s, timerRunning) {continue}// Note that runOneTimer may temporarily unlock// pp.timersLock.runOneTimer(pp, t, now)return 0case timerDeleted:if !atomic.Cas(&t.status, s, timerRemoving) {continue}// 删除timerdodeltimer0(pp)if !atomic.Cas(&t.status, timerRemoving, timerRemoved) {badTimer()}atomic.Xadd(&pp.deletedTimers, -1)if len(pp.timers) == 0 {return -1}case timerModifiedEarlier, timerModifiedLater:if !atomic.Cas(&t.status, s, timerMoving) {continue}// 先删除再重新添加 调整顺序t.when = t.nextwhendodeltimer0(pp)doaddtimer(pp, t)if !atomic.Cas(&t.status, timerMoving, timerWaiting) {badTimer()}case timerModifying:// Wait for modification to complete.osyield()case timerNoStatus, timerRemoved:// Should not see a new or inactive timer on the heap.badTimer()case timerRunning, timerRemoving, timerMoving:// These should only be set when timers are locked,// and we didn't do it.badTimer()default:badTimer()}}
}

再看一下runOneTimer这个方法

// runOneTimer runs a single timer.
// The caller must have locked the timers for pp.
// This will temporarily unlock the timers while running the timer function.
//
//go:systemstack
func runOneTimer(pp *p, t *timer, now int64) {if raceenabled {ppcur := getg().m.p.ptr()if ppcur.timerRaceCtx == 0 {ppcur.timerRaceCtx = racegostart(abi.FuncPCABIInternal(runtimer) + sys.PCQuantum)}raceacquirectx(ppcur.timerRaceCtx, unsafe.Pointer(t))}f := t.farg := t.argseq := t.seq// 注意就是ticker的情况if t.period > 0 {// Leave in heap but adjust next time to fire.delta := t.when - nowt.when += t.period * (1 + -delta/t.period)if t.when < 0 { // check for overflow.t.when = maxWhen}siftdownTimer(pp.timers, 0)// 再次将运行中切换为等待if !atomic.Cas(&t.status, timerRunning, timerWaiting) {badTimer()}updateTimer0When(pp)} else {// Remove from heap.dodeltimer0(pp)if !atomic.Cas(&t.status, timerRunning, timerNoStatus) {badTimer()}}if raceenabled {// Temporarily use the current P's racectx for g0.gp := getg()if gp.racectx != 0 {throw("runOneTimer: unexpected racectx")}gp.racectx = gp.m.p.ptr().timerRaceCtx}unlock(&pp.timersLock)// 执行方法f(arg, seq)lock(&pp.timersLock)if raceenabled {gp := getg()gp.racectx = 0}
}

这f就是

// sendTime does a non-blocking send of the current time on c.
func sendTime(c any, seq uintptr) {select {case c.(chan Time) <- Now():default:}
}

也就是向监听的channel发送一个当前时间。也就是触发了我们select的动作。

【go语言调度之timer】相关推荐

  1. 并发问题的解决思路以及Go语言调度器工作原理

    上周的文章<Go并发编程里的数据竞争以及解决之道>最后留下了一个用并发解决的思考题,期间有几位同学留言说了自己的实现思路,也有两位直接私信发代码让我看的,非常感谢几位的积极参与.今天的文章 ...

  2. 上周并发题的解题思路以及介绍Go语言调度器

    上周的文章<Go并发编程里的数据竞争以及解决之道>最后留下了一个用并发解决的思考题,期间有几位同学留言说了自己的实现思路,也有两位直接私信发代码让我看的,非常感谢几位的积极参与.今天的文章 ...

  3. Go语言调度器之调度main goroutine(14)

    本文是<Go语言调度器源代码情景分析>系列的第14篇,也是第二章的第4小节. 上一节我们通过分析main goroutine的创建详细讨论了goroutine的创建及初始化流程,这一节我们 ...

  4. Go 语言调度(一): 系统调度

    原文作者:达菲格 来源:简书 调度相关的一系列文章主要参考 Scheduling In Go : Part I - OS Scheduler 翻译来的. 因为在学习的过程中偶然发现,感觉总结得蛮好的, ...

  5. Go 语言调度(三): 并发

    作者:达菲格 来源:简书 介绍 当我解决问题时,尤其是新问题,我不会一上来就想着能不能使用并发来处理.我会首先想出一个顺序执行的解决方案.然后在可读性和技术评审后,我会开始考虑并发处理能不能让问题得到 ...

  6. java调度:(三)Timer中的单线程守护

    早听说Timer中是单守护进程在执行所有的任务,在调度的时候会有一些问题,现做下面例子来测试: 首先做两个简单的任务: 1. TimerJOB1 package test;import java.ut ...

  7. go语言调度器源代码情景分析之五:汇编指令

    本文是<go调度器源代码情景分析>系列 第一章 预备知识的第4小节. 汇编语言是每位后端程序员都应该掌握的一门语言,因为学会了汇编语言,不管是对我们调试程序还是研究与理解计算机底层的一些运 ...

  8. Go (Golang) 语言-Golang 定时器Timer和Ticker、time.AfterFunc、time.NewTicker()实例

    文章目录 Golang 定时器Timer和Ticker time.Timer time.NewTimer()实例 time.AfterFunc time.Ticker time.NewTicker() ...

  9. Go 语言调度(二): goroutine 调度器

    原文作者:达菲格 来源:简书 介绍 上一篇文章我对操作系统级别的调度进行了讲解,这对理解 Go 语言的调度器是很重要的.这篇文章,我将解释下 Go 语言的调度器是如何工作的.依旧专注在上层抽象的基本概 ...

最新文章

  1. pandas.read_csv(path_features_known_csv, header=None)的用法
  2. linux中文件描述符fd和文件指针flip的理解
  3. Android UI:XML文件配置按钮等背景方案
  4. JavaWeb(九)——JavaBean、Filter
  5. 我的cookie读写
  6. java中集合的结构list类型
  7. 机器如何区分和判定指令和数据
  8. steam夏日促销用Python爬取排行榜上的游戏打折信息
  9. HTTP Response Splitting攻击探究 转
  10. 清华提出:用于细粒度实体分类的Prompt-Learning,并提出可训练Prompt模板
  11. [Asp.net]常见数据导入Excel,Excel数据导入数据库解决方案,总有一款适合你!
  12. 现实世界的Windows Azure:采访Definition 6首席技术官Paul Hernacki
  13. C语言自己写代码实现的strcmp函数
  14. Webservice接口调用工具类
  15. 程序员纷纷“跑路”一线城市,背后的原因是?丨黑马观察
  16. 向量代数:向量加法、共线与共面
  17. 论OSPF中ASBR和ABR
  18. Spark大数据技术与应用期末总结大题
  19. python斐波那契螺旋线怎么画向日葵心_斐波那契螺旋线的图形作法
  20. Mooc_AutoCAD绘制建筑施工图_单元平面图测验题

热门文章

  1. android 通讯录恢复,安卓手机通讯录丢失怎么找回?四个步骤教会你!
  2. FL STUDIO 21 水果音乐制作软件fl V21Producer 制作人版多少钱
  3. 手撕 Golang 高性能内存缓存库 bigcache! #4
  4. 联想z470安装深度linux,联想 z470 成功安装 mac os EI Capitan 10.11.6
  5. 当eclipse工程出现红色感叹号的解决方法
  6. Windows10 如何关闭 Windows Defender (Antimalware Service Executable)
  7. 计算机版图设计培训,版图设计培训资料.ppt
  8. 中琅条码打印软件之如何导出条形码或二维码矢量文件到Coredraw
  9. 第一次参加DC比赛总结
  10. IOS – OpenGL ES 图像球形折射 GPUImageSphereRefractionFilter