三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

Go语言timer源码:时间调度实现深度解析

Go语言timer源码:时间调度实现深度解析

Go语言timer源码:时间调度实现深度解析

一、引言:定时器的重要性

在Go语言中,定时器是实现超时控制、延迟任务、周期执行等功能的基础。无论是HTTP请求的超时处理,还是缓存的自动过期,都离不开定时器的支持。

Go语言的定时器实现采用了**四叉堆(min-heap)**数据结构,能够高效地管理大量定时任务。本文将深入剖析Go语言定时器的源码实现。

二、定时器核心数据结构

2.1 定时器结构

type timer struct { i int // 在堆中的索引 when int64 // 触发时间(纳秒) period int64 // 周期(纳秒),0表示一次性定时器 f func(interface{}, uintptr) // 回调函数 arg interface{} // 回调参数 seq uintptr // 序列号 }

2.2 全局定时器结构

type timers struct { lock mutex g *g // 负责处理定时器的goroutine created bool sleeping bool // 是否在睡眠等待 rescheduling bool // 是否在重新调度 waitnote note t []*timer // 定时器堆(四叉堆) }

三、四叉堆实现原理

3.1 堆的基本概念

四叉堆是一种完全二叉堆的扩展,每个父节点有四个子节点。这种结构特别适合定时器场景:

// 四叉堆的父子关系计算 // 对于索引为i的节点: // - 父节点索引:(i-1)/4 // - 子节点起始索引:4*i + 1 // - 四个子节点:4*i+1, 4*i+2, 4*i+3, 4*i+4 func parent(i int) int { return (i - 1) / 4 } func left(i int) int { return 4*i + 1 } func right(i int) int { return 4*i + 4 }

3.2 堆的上浮操作

func up(t []*timer, i int) { for i > 0 { p := parent(i) if i == 0 || t[p].when <= t[i].when { break } t[p], t[i] = t[i], t[p] t[p].i = p t[i].i = i i = p } }

3.3 堆的下沉操作

func down(t []*timer, i int, n int) { for { j := min(t, i, n) if j == i { break } t[i], t[j] = t[j], t[i] t[i].i = i t[j].i = j i = j } } func min(t []*timer, i int, n int) int { m := i for k := left(i); k < left(i)+4 && k < n; k++ { if k < n && t[k].when < t[m].when { m = k } } return m }

四、定时器添加与删除

4.1 添加定时器

func addtimer(t *timer) bool { t.i = len(timers.t) timers.t = append(timers.t, t) up(timers.t, t.i) return true }

添加定时器的过程:

  1. 将新定时器添加到堆末尾
  2. 执行上浮操作,恢复堆性质

4.2 删除定时器

func deltimer(t *timer) bool { d := timerDeleted atomic.Store(unsafe.Pointer(&t.status), unsafe.Pointer(&d)) return true } func cleantimers(tp *timers) { td := timers.t for { if len(td) == 0 { return } t := td[0] if t.status != timerDeleted { return } // 将最后一个元素移到顶部 last := len(td) - 1 if last > 0 { td[0] = td[last] td[0].i = 0 } td[last] = nil td = td[:last] if last > 0 { down(td, 0, last) } } }

五、定时器运行机制

5.1 定时器调度循环

func runtimer(t []*timer, now int64) int64 { for { if len(t) == 0 { return -1 } t0 := t[0] if t0.status != timerWaiting { if t0.status == timerDeleted { // 跳过已删除的定时器 last := len(t) - 1 if last > 0 { t[0] = t[last] t[0].i = 0 } t[last] = nil t = t[:last] if last > 0 { down(t, 0, last) } continue } } return t0.when } }

5.2 定时器执行

func runOneTimer(pp *p, t *timer, now int64) { // 标记为正在执行 t.status = timerRunning // 保存当前goroutine和参数 gp := getg() capture := t.arg seq := t.seq // 执行回调函数 t.f(t.arg, t.seq) // 如果是周期性定时器,重新计算触发时间 if t.period > 0 { t.when = now + t.period // 更新堆中的位置 updateTimerHeap(t) } else { // 一次性定时器,标记为已删除 t.status = timerDeleted } }

六、NewTimer与After实现

6.1 NewTimer实现

func NewTimer(d time.Duration) *Timer { c := make(chan Time, 1) t := &timer{ f: sendTime, arg: c, when: now() + int64(d), } addtimer(t) return &Timer{C: c, r: t} } func sendTime(c chan Time, arg interface{}) { c <- Time{} }

6.2 After实现

func After(d time.Duration) <-chan Time { return NewTimer(d).C }

6.3 AfterFunc实现

func AfterFunc(d time.Duration, f func()) *Timer { t := &timer{ f: goFunc, arg: f, when: now() + int64(d), } addtimer(t) return &Timer{r: t} } func goFunc(arg interface{}, seq uintptr) { go arg.(func())() }

七、Ticker实现

7.1 Ticker结构

type Ticker struct { C <-chan Time r *timer } func NewTicker(interval time.Duration) *Ticker { if interval <= 0 { panic(errors.New("non-positive interval for NewTicker")) } c := make(chan Time, 1) t := &timer{ f: sendTime, arg: c, when: now() + int64(interval), period: int64(interval), // 设置周期 } addtimer(t) return &Ticker{C: c, r: t} }

7.2 Ticker停止

func (t *Ticker) Stop() { stopTimer(t.r) }

八、超时控制的最佳实践

8.1 HTTP请求超时

func fetchWithTimeout(url string, timeout time.Duration) ([]byte, error) { ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() req, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { return nil, err } resp, err := http.DefaultClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() return io.ReadAll(resp.Body) }

8.2 优雅关闭超时

func gracefulShutdown(server *http.Server, timeout time.Duration) { ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() if err := server.Shutdown(ctx); err != nil { log.Printf("Server shutdown error: %v", err) } }

8.3 重试机制

func retryWithBackoff(fn func() error, maxRetries int) error { var err error for i := 0; i < maxRetries; i++ { if err =
← 返回列表