GoLang中的timer定时器实现原理分析
本篇文章主要是结合我之前面试的各种经历和实战开发中遇到的问题解决经验整理的,希望这篇《GoLang中的timer定时器实现原理分析》对你有很大帮助!欢迎收藏,分享给更多的需要的朋友学习~
// NewTimer creates a new Timer that will send
// the current time on its channel after at least duration d.
func NewTimer(d Duration) *Timer {
c := make(chan Time, 1)
t := &Timer{
C: c,
r: runtimeTimer{
when: when(d),
f: sendTime,
arg: c,
},
}
startTimer(&t.r)
return t
}
// The Timer type represents a single event.
// When the Timer expires, the current time will be sent on C,
// unless the Timer was created by AfterFunc.
// A Timer must be created with NewTimer or AfterFunc.
type Timer struct {
C func NewTicker(d Duration) *Ticker {
if d ticker 跟 timer 的初始化过程差不多,但是 ticker 比 timer 多了一个 period 参数,意为间隔的意思。
// Interface to timers implemented in package runtime.
// Must be in sync with ../runtime/time.go:/^type timer
type runtimeTimer struct {
pp uintptr
when int64 //触发时间
period int64 //执行周期性任务的时间间隔
f func(any, uintptr) // 执行的回调函数,NOTE: must not be closure
arg any //执行任务的参数
seq uintptr //回调函数的参数,该参数仅在 netpoll 的应用场景下使用
nextwhen int64 //如果是周期性任务,下次执行任务时间
status uint32 //状态
}
// sendTime does a non-blocking send of the current time on c.
func sendTime(c any, seq uintptr) {
select {
case c.(chan Time) sendTime 采用非阻塞的形式,意为,不管是否存在接收方,此定时器一旦到时间了就要触发掉。
// runtime/runtime2.go
type p struct {
.....
// The when field of the first entry on the timer heap.
// This is updated using atomic functions.
// This is 0 if the timer heap is empty.
// 堆顶元素什么时候执行
timer0When uint64
// The earliest known nextwhen field of a timer with
// timerModifiedEarlier status. Because the timer may have been
// modified again, there need not be any timer with this value.
// This is updated using atomic functions.
// This is 0 if there are no timerModifiedEarlier timers.
// 如果有timer修改为更早执行时间了,将会将执行时间更新到更早时间
timerModifiedEarliest uint64
// Lock for timers. We normally access the timers while running
// on this P, but the scheduler can also do it from a different P.
// 操作timer的互斥锁
timersLock mutex
// Actions to take at some time. This is used to implement the
// standard library's time package.
// Must hold timersLock to access.
//该p 上的所有timer,必须加锁去操作这个字段,因为不同的p 操作这个字段会有竞争关系
timers []*timer
// Number of timers in P's heap.
// Modified using atomic instructions.
//p 堆上所有的timer数
numTimers uint32
// Number of timerDeleted timers in P's heap.
// Modified using atomic instructions.
//被标记为删除的timer,要么是我们调用stop,要么是timer 自己触发后过期导致的删除
deletedTimers uint32
}
// runtime/time.go
type timer struct {
// If this timer is on a heap, which P's heap it is on.
// puintptr rather than *p to match uintptr in the versions
// of this struct defined in other packages.
pp puintptr
// Timer wakes up at when, and then at when+period, ... (period > 0 only)
// each time calling f(arg, now) in the timer goroutine, so f must be
// a well-behaved function and not block.
//
// when must be positive on an active timer.
when int64
period int64
f func(any, uintptr)
arg any
seq uintptr
// What to set the when field to in timerModifiedXX status.
nextwhen int64
// The status field holds one of the values below.
status uint32
}
// startTimer adds t to the timer heap.
//go:linkname startTimer time.startTimer
func startTimer(t *timer) {
if raceenabled {
racerelease(unsafe.Pointer(t))
}
addtimer(t)
}
// stopTimer stops a timer.
// It reports whether t was stopped before being run.
//go:linkname stopTimer time.stopTimer
func stopTimer(t *timer) bool {
return deltimer(t)
}
// addtimer adds a timer to the current P.
// This should only be called with a newly created timer.
// That avoids the risk of changing the when field of a timer in some P's heap,
// which could cause the heap to become unsorted.
func addtimer(t *timer) {
// when must be positive. A negative value will cause runtimer to
// overflow during its delta calculation and never expire other runtime
// timers. Zero will cause checkTimers to fail to notice the timer.
if t.when timer 存储在P中的 timers []*timer成员属性上。timers看起来是一个切片,但是它是按照runtimeTimer.when这个数值排序的小顶堆四叉树,触发时间越早越排在前面。

整体来讲就是父节点一定比其子节点小,子节点之间没有任何关系和大小的要求。
关于acquirem和releasem
//go:nosplit
func acquirem() *m {
_g_ := getg()
_g_.m.locks++
return _g_.m
}
//go:nosplit
func releasem(mp *m) {
_g_ := getg()
mp.locks--
if mp.locks == 0 && _g_.preempt {
// restore the preemption request in case we've cleared it in newstack
_g_.stackguard0 = stackPreempt
}
}
acquirem函数获取当前M,并禁止M被抢占,因为M被抢占时的判断如下
//C:\Go\src\runtime\preempt.go +287
func canPreemptM(mp *m) bool {
return mp.locks == 0 && mp.mallocing == 0 && mp.preemptoff == "" && mp.p.ptr().status == _Prunning
}
- 运行时没有禁止抢占(
m.locks == 0) - 运行时没有在执行内存分配(
m.mallocing == 0) - 运行时没有关闭抢占机制(
m.preemptoff == "") - M 与 P 绑定且没有进入系统调用(
p.status == _Prunning)
timers的触发
// runtime/proc.go func checkTimers(pp *p, now int64) (rnow, pollUntil int64, ran bool) // runtime/time.go func runtimer(pp *p, now int64) int64 func runOneTimer(pp *p, t *timer, now int64)
runtime/time.go文件中提供了checkTimers/runtimer/runOneTimer三个方法。checkTimers方法中,如果当前p的timers长度不为0,就不断地调用runtimers。runtimes会根据堆顶的timer的状态判断其能否执行,如果可以执行就调用runOneTimer实际执行。
触发定时器的途径有两个
- 通过调度器在调度时进行计时器的触发,findrunnable, schedule, stealWork。
- 通过系统监控检查并触发计时器(到期未执行),sysmon。
调度器的触发一共分两种情况,一种是在调度循环的时候调用 checkTimers 方法进行计时器的触发。另外一种是当前处理器 P 没有可执行的 Timer,且没有可执行的 G。那么按照调度模型,就会去窃取其他计时器和 G。
即使是通过每次调度器调度和窃取的时候触发,但毕竟是具有一定的随机和不确定性,因此系统监控触发依然是一个兜底保障,在 Go 语言中 runtime.sysmon 方法承担了这一个责任,存在触发计时器的逻辑,在每次进行系统监控时,都会在流程上调用 timeSleepUntil 方法去获取下一个计时器应触发的时间,以及保存该计时器已打开的计时器堆的 P。
在获取完毕后会马上检查当前是否存在 GC,若是正在 STW 则获取调度互斥锁。若发现下一个计时器的触发时间已经过去,则重新调用 timeSleepUntil 获取下一个计时器的时间和相应 P 的地址。检查 sched.sysmonlock 所花费的时间是否超过 50μs。若是,则有可能前面所获取的下一个计时器触发时间已过期,因此重新调用 timeSleepUntil 方法再次获取。如果发现超过 10ms 的时间没有进行 netpoll 网络轮询,则主动调用 netpoll 方法触发轮询。同时如果存在不可抢占的处理器 P,则调用 startm 方法来运行那些应该运行,但没有在运行的计时器。
今天关于《GoLang中的timer定时器实现原理分析》的内容就介绍到这里了,是不是学起来一目了然!想要了解更多关于golang的内容请关注golang学习网公众号!
MySQL安装提示配置信息已损坏请联系技术人员
- 上一篇
- MySQL安装提示配置信息已损坏请联系技术人员
- 下一篇
- MySQL 8.0.31中使用MySQL Workbench提示配置文件错误信息解决方案
-
- 自觉的服饰
- 这篇文章出现的刚刚好,很详细,很好,码起来,关注作者大大了!希望作者大大能多写Golang相关的文章。
- 2023-03-27 13:20:39
-
- Golang · Go教程 | 31分钟前 | 测试 · go · 覆盖率 · Go Go test go tool cover 函数覆盖率
- Go 覆盖率怎么按函数查看而不是只看总百分比
- 336浏览 收藏
-
- Golang · Go教程 | 40分钟前 |
- Go test 怎么用 -run 和 -count 精确重跑一个测试
- 182浏览 收藏
-
- Golang · Go教程 | 55分钟前 |
- Go fuzz 测试怎么保存可复现的失败输入
- 326浏览 收藏
-
- Golang · Go教程 | 1小时前 | go · testing · 并行测试 Go测试 文件测试 testing.T.TempDir
- Go testing.T.TempDir 怎么隔离文件测试的临时目录
- 263浏览 收藏
-
- Golang · Go教程 | 1小时前 | 标准库 · go · 编码转换 · Go 十六进制 encoding/hex 二进制ID
- Go encoding/hex 怎么把二进制 ID 稳定转换成小写文本
- 477浏览 收藏
-
- Golang · Go教程 | 1小时前 | Go教程 · URL安全 · 编码转换 · base64 Go URL参数 encoding/base64
- Go base64 解码 URL 参数时怎么处理无填充格式
- 405浏览 收藏
-
- Golang · Go教程 | 2小时前 |
- Go TLS 客户端怎么加载内存证书池而不读系统证书
- 343浏览 收藏
-
- Golang · Go教程 | 2小时前 | 网络编程 · 错误处理 · Go教程 · DNS超时 · DNS Go context net.Resolver LookupHost
- Go DNS 查询怎么设置独立超时并区分临时错误
- 103浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- SuperCLUE
- SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
- 172次使用
-
- C-Eval
- 深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
- 102次使用
-
- AI Prompt Library
- 探索AI Prompt Library免费资源库,涵盖营销、写作及多场景AI提示词。兼容ChatGPT、Claude等工具,一键复制优化输出,提升工作效率。
- 28次使用
-
- LangGPT
- LangGPT是一种受编程语言启发的结构化提示词设计工具,提供双层框架、模块化模板及变量功能,帮助用户高效编写高质量Prompt。该项目已在GitHub免费开源,适用于内容创作、编程辅助等多场景。
- 40次使用
-
- ClickPrompt
- ClickPrompt是一款专为AI提示词编写者设计的开源在线工具,支持Stable Diffusion绘图、ChatGPT对话及GitHub Copilot代码辅助。提供Prompt自动生成、一键运行、社区分享及可视化优化功能,帮助用户高效获取精准AI输出。
- 76次使用
-
- Golangcron定时器和定时任务的使用场景
- 2023-01-28 208浏览
-
- golang一次性定时器Timer用法及实现原理详解
- 2023-01-07 335浏览
-
- Go Ticker 周期性定时器用法及实现原理详解
- 2023-01-07 417浏览
-
- go语言定时器Timer及Ticker的功能使用示例详解
- 2023-01-19 467浏览
-
- golangcroncli定时器命令详解
- 2022-12-23 472浏览
