定时器

1.定时器结构函数

type Timer struct {
    C <-chan Time       // 接受定时器事件的通道
    r runtimeTimer
}

type runtimeTimer struct {
    tb uintptr
    i  int

    when   int64
    period int64
    f      func(interface{}, uintptr) // NOTE: must not be closure
    arg    interface{}
    seq    uintptr
}

2.建立定时器ui

func NewTimer(d Duration) *Timer
var timer = NewTimer(time.Second)

go func() {
    for {
        select {
        case <-timer.C:
            fmt.Println("time out.")
        }
    }
}()
func NewTimer(d Duration) *Timer {
    c := make(chan Time, 1)     // 建立一个带有一个Time结构缓冲的通道
    t := &Timer{
        C: c,
        r: runtimeTimer{        // 运行时定时器
            when: when(d),      // 定时多久
            f:    sendTime,     // Golang写入时间的回调接口
            arg:  c,            // 往哪一个通道写入时间
        },
    }
    startTimer(&t.r)            // 启动提交定时器
    return t
}

// 时间到后,Golang自动调用sendTime接口,尝试往c通道写入时间
func sendTime(c interface{}, seq uintptr) {
    // 给c通道以非阻塞方式发送时间
    // 若是被用于NewTimer, 不管如何不能阻塞.
    // 若是被用于NewTicker,接收方未及时接受时间,则会丢弃掉,由于发送时间是周期性的。
    select {
    case c.(chan Time) <- Now():
    default:
    }
}

func startTimer(*runtimeTimer)
package main

import (
    "fmt"
    "time"
)

func main() {

    // 建立延迟3s的定时器
    exit := make(chan bool)
    timer := time.NewTimer(3 * time.Second)

    go func() {
        defer func() {
            exit <- true
        }()

        select {
        case <-timer.C:
            fmt.Println("time out.")
            return
        }
    }()

    <-exit
}

3.中止定时器blog

func (t *Timer) Stop() bool
if !t.Stop() {
    <-t.C
}
func stopTimer(*runtimeTimer) bool
package main

import (
    "fmt"
    "time"
)

func main() {
    timer := time.NewTimer(time.Second)
    time.Sleep(time.Millisecond * 500)
    timer.Stop()

    fmt.Println("timer stopped")
    time.Sleep(time.Second * 3)
}

4.重置定时器

func (t *Timer) Reset(d Duration) bool
package main

import (
    "fmt"
    "time"
)

func doTimer(t *time.Timer, exit chan<- bool) {

    go func(t *time.Timer) {
        defer func() {
            exit <- true
        }()

        for {
            select {
            case c := <-t.C:
                fmt.Println("timer timeout at", c)
                return
            }defer func() {
        ticker.Stop()
        fmt.Println("ticker stopped")
    } ()
        }
    }(t)
}

func main() {
    sign := make(chan bool)
    timer := time.NewTimer(time.Second * 3)

    doTimer(timer, sign)
    time.Sleep(time.Second)

    // 实际测试:注释下面三行代码,效果同样。
    if !timer.Stop() {
        <-timer.C
    }

    timer.Reset(time.Second * 3)
    fmt.Println("timer reset at", time.Now())

    <-sign
}

5.After接口

func After(d Duration) <-chan Time
package main

import (
    "fmt"
    "time"
)

func main() {
    sign := make(chan bool)
    chan1 := make(chan int)
    chan2 := make(chan int)

    defer func() {
        close(sign)
        close(chan1)
        close(chan2)
    }()

    go func() {
        for {
            select {
            case c := <-time.After(time.Second * 3):
                fmt.Println("After at", c)
                // 若不往sign通道写入数据,程序循环每隔3s执行当前case分支。
                sign <- true
            case c1 := <-chan1:
                fmt.Println("c1", c1)
            case c2 := <-chan2:
                fmt.Println("c1", c2)
            }
        }
    }()

    <-sign
}

6.AfterFun接口

func AfterFunc(d Duration, f func()) *Timer
package main

import (
    "fmt"
    "time"
)

func main() {

    timer := time.AfterFunc(time.Second*3, func() {
        fmt.Println("AfterFunc Callback")
    })

    time.Sleep(time.Second * 5)
    timer.Stop()
}

断续器

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

type runtimeTimer struct {
    tb uintptr
    i  int

    when   int64
    period int64
    f      func(interface{}, uintptr) // NOTE: must not be closure
    arg    interface{}
    seq    uintptr
}
var ticker = time.NewTicker(time.Second)
var ticker = time.NewTicker(time.Second)
ticker.Stop()

实例一:使用Ticker(并使用时间控制ticker)

package main
import (
    "fmt"
    "time"
)

func TickerTest() *time.Ticker {
    // 建立一个断续器
    var ticker = time.NewTicker(time.Second)

    go func() {
        // 使用for + range组合处理断续器
        for t := range ticker.C {
            fmt.Println("tick at", t)
        }
    }()

    return ticker
}

func main() {
    ticker := TickerTest()
    time.Sleep(time.Second * 10)
    ticker.Stop()        
}

实例二:使用channel控制ticker

package main

import (
    "fmt"
    "time"
)

func DoTicker(ticker *time.Ticker) chan<- bool {
    stopChan := make(chan bool)

    go func(ticker *time.Ticker) {
        // 注册中止ticker方法
        defer ticker.Stop()
        for {
            select {
            // 处理断续器事件
            case t := <-ticker.C:
                fmt.Println("tick at", t)
            // 接受外部中止断续器事件
            case stop := <-stopChan:
                if stop {
                    fmt.Println("DoTicker Exit")
                    return
                }
            }
        }
    }(ticker)

    // 返回由外部控制Ticker中止的Channel
    return stopChan
}

func main() {

    var ticker = time.NewTicker(time.Second)
    stopChan := DoTicker(ticker)

    time.Sleep(time.Second * 10)
    // 中止断续器
    stopChan <- true
    time.Sleep(time.Second) 
    close(stopChan)
}

实例三:使用channel控制中止ticker

package main

import (
    "fmt"
    "time"
)

func DoTicker(ticker *time.Ticker, times int) {
    // 建立有times个缓冲的byte通道
    stopChan := make(chan byte, times)

    go func(ticker *time.Ticker) {

        defer func() {
            // 通过调试,defer语句块并未执行
            ticker.Stop()
            fmt.Println("ticker stopped")
        } ()

        for t := range ticker.C {
            fmt.Println("write stop channel")

            // 写满times次后,当前goroutine自动退出
            stopChan <- 0
            fmt.Println("tick at", t)
        }

        // 经调试,该语句并未执行
        fmt.Println("DoTicker1 Exit")
    }(ticker)
}

func main() {
    var ticker = time.NewTicker(time.Second)

    DoTicker(ticker, 5)
    time.Sleep(time.Second * 10)
}
write stop channel
tick at 2019-03-13 11:44:35.932692894 +0800 CST m=+1.000442776
write stop channel
tick at 2019-03-13 11:44:36.932643384 +0800 CST m=+2.000393270
write stop channel
tick at 2019-03-13 11:44:37.932565147 +0800 CST m=+3.000315031
write stop channel
tick at 2019-03-13 11:44:38.932735589 +0800 CST m=+4.000485469
write stop channel
tick at 2019-03-13 11:44:39.932553565 +0800 CST m=+5.000303443
write stop channel

Process finished with exit code 0