zoukankan      html  css  js  c++  java
  • 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

     
    一个没有高级趣味的人。 email:hushui502@gmail.com
  • 相关阅读:
    Splay专题总结
    UVa12657
    ZOJ3772
    POJ1743
    高斯消元模板
    python使用chrom登陆微博
    mysql常用数据库(表)命令
    mysql索引
    mysql建表的时候,时间戳的选用
    php 金额每三位添加一个逗号
  • 原文地址:https://www.cnblogs.com/CherryTab/p/12728171.html
Copyright © 2011-2022 走看看