TOC

Go Ticker For Range

Ticker 本质就是一个通道,可以用 for select 接收,也可以直接 for range 接收。

go func() {
    ticker := time.Tick(1 * time.Second)
    for {
        select {
        case <-ticker:
            // do something
        }
    }
}()

go func() {
    ticker := time.Tick(1 * time.Second)
    for range ticker {
        // do something
    }
}()

附:Ticker

src\time\tick.go

type Ticker struct {
    C <-chan Time // The channel on which the ticks are delivered.
    r runtimeTimer
}

func NewTicker(d Duration) *Ticker {
    if d <= 0 {
        panic(errors.New("non-positive interval for NewTicker"))
    }
    // Give the channel a 1-element time buffer.
    // If the client falls behind while reading, we drop ticks
    // on the floor until the client catches up.
    c := make(chan Time, 1)
    t := &Ticker{
        C: c,
        r: runtimeTimer{
            when:   when(d),
            period: int64(d),
            f:      sendTime,
            arg:    c,
        },
    }
    startTimer(&t.r)
    return t
}

src\time\sleep.go

type runtimeTimer struct {
    pp       uintptr
    when     int64
    period   int64
    f        func(any, uintptr) // NOTE: must not be closure
    arg      any
    seq      uintptr
    nextwhen int64
    status   uint32
}

func sendTime(c any, seq uintptr) {
    select {
    case c.(chan Time) <- Now():
    default:
    }
}

src\runtime\time.go

// startTimer adds t to the timer heap.
//
//go:linkname startTimer time.startTimer
func startTimer(t *timer) {
    if raceenabled {
        racerelease(unsafe.Pointer(t))
    }
    addtimer(t)
}

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 <= 0 {
        throw("timer when must be positive")
    }
    if t.period < 0 {
        throw("timer period must be non-negative")
    }
    if t.status.Load() != timerNoStatus {
        throw("addtimer called with initialized timer")
    }
    t.status.Store(timerWaiting)

    when := t.when

    // Disable preemption while using pp to avoid changing another P's heap.
    mp := acquirem()

    pp := getg().m.p.ptr()
    lock(&pp.timersLock)
    cleantimers(pp)
    doaddtimer(pp, t)
    unlock(&pp.timersLock)

    wakeNetPoller(when)

    releasem(mp)
}

func doaddtimer(pp *p, t *timer) {
    // Timers rely on the network poller, so make sure the poller
    // has started.
    if netpollInited.Load() == 0 {
        netpollGenericInit()
    }

    if t.pp != 0 {
        throw("doaddtimer: P already set in timer")
    }
    t.pp.set(pp)
    i := len(pp.timers)
    pp.timers = append(pp.timers, t)
    siftupTimer(pp.timers, i)
    if t == pp.timers[0] {
        pp.timer0When.Store(t.when)
    }
    pp.numTimers.Add(1)
}

在 p 的 timers 中插入一个 timer 定时器。

开发一个 Golang 编程框架

  1. 参照 golang-standards/project-layout 的项目结构
  2. 使用 go mod 管理依赖(尽量少用第三方依赖,使用原生库)
  3. 支持 HTTP API 和 gRPC API(也需要对应的客户端实现)
  4. 在 HTTP API 的基础上增加 RESTful 层
  5. 使用 Viper 做配置管理(配置文件、环境变量、命令行参数、etcd)
  6. 使用 slog 做日志输出
  7. 使用 wire 做依赖注入
  8. 内置雪花算法、JWT、RBAC、常见加密算法等实现(工具库)
  9. 支持 MySQL / Redis / RabbitMQ
  10. 支持 S3 存储
  11. 内置定时任务框架
  12. 支持配置中心(内置 etcd 实现)
  13. 支持服务注册(内置 etcd 实现)
如果你有魔法,你可以看到一个评论框~