Go的Timer

线上听了欧神的分享,就当算是个笔记吧。

 

Timer顾名思义就是定时器的意思,比如我们可以不借助消息队列中间件,单纯的用timer也可以实现一些定时功能。用法不提了,写一下1.4的分析。

 

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  // 这里主要是和P绑定,减少数据竞争

    // 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   int64
    period int64
    f      func(interface{}, uintptr)
    arg    interface{}
    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
}

 

// 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 <-chan Time  // 这就是典型的管道通信,在一个P里。
    r runtimeTimer
}

 

// 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
}

 

 

func sendTime(c interface{}, seq uintptr) {
    // Non-blocking send of time on c.
    // Used in NewTimer, it cannot block anyway (buffer).
    // Used in NewTicker, dropping sends on the floor is
    // the desired behavior when the reader gets behind,
    // because the sends are periodic.
    select {
    case c.(chan Time) <- Now():    
    default:
    }
}

 

type p struct {
   (...)
   timersLock mutex
   timers []*timer  // 虽然是数组,但实际上底层是堆实现
   (...)
}

 

func schedule() {
   _g_ := getg()

   (...)

top:
   pp := _g_.m.p.ptr()
   (...)

   checkTimers(pp, 0)  // 每次调度都要查看这个p绑定的timer

   (...)
   execute(...)
}

 

func runtimer(pp *p, now int64) int64 {
   for {
       t := pp.timers[0]    // 看顶部的第一个
       (...)
       switch s := atomic.Load(&t.status); s {
       case timerWaiting:
           if t.when > now {
               return t.when
           }
           (...)
           runOneTimer(pp, t, now)
           return 0
(...)
       }
   }
}

 

func runOneTimer(pp *p, t *timer, now int64) {
   (...)
   f := t.f
   arg := t.arg
   seq := t.seq
   dodeltimer0(pp)
   atomic.Cas(&t.status, timerRunning, timerNoStatus)
   (...)
   unlock(&pp.timersLock)
   f(arg, seq) // 触发 sendTime 信号 通知用户 goroutine
   lock(&pp.timersLock)
   (...)
}

 

 

 

欧神画的Timer状态机

 

实际上细枝末节的东西还很多,比如把当前的timer要执行的g分到别的p去执行等。

 

https://changkun.de/golang/zh-cn/part2runtime/ch06sched/timer/

 

end

 
posted @ 2020-04-18 21:21  zhangyu63  阅读(216)  评论(0编辑  收藏  举报