定时器

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.创建定时器

  • 接口定义

    func NewTimer(d Duration) *Timer
  • 使用简单实例

    var timer = NewTimer(time.Second)
    
    go func() {
    for {
    select {
    case <-timer.C:
    fmt.Println("time out.")
    }
    }
    }()
  • NewTimer源代码:

    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.停止定时器

  • 接口定义

    func (t *Timer) Stop() bool
    • 本接口可以防止计时器出触发。如果定时器停止,则返回true,如果定时器已过期或已停止,则返回false。

      Stop不关闭通道,以防止通道读取的操作不正确。

    • 为防止通过NewTimer创建的定时器,在调用Stop接口后触发,检查Stop返回值并清空通道。如:

      if !t.Stop() {
      <-t.C
      }

      但不能与Timer的通道中的其他接受同时进行!!!

    • 对于使用AfterFunc(d, f)创建的定时器,如果t.Stop()返回false,则定时器已经过期,并且函数f已经在自己的协程中启动。

      无论函数f是否执行完成,Stop()返回不会阻塞,如果用户需要知道f是否执行完毕,必须明确地与f协调。

  • 内部使用接口

    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
    • 定时器被激活,返回true,若定时器已过期或已被停止,返回false。

    • 若程序已从t.C中接受数据,定时器已知过期,t.Rest()可直接使用

    • 若程序还尚未从t.C收到一个值,则必须停止定时器。如果Stop提示计时器在停止之前已过期,则应明确清空通道。

      if !t.Stop() {
      <-t.c
      }
      t.Rest(d)
  • 代码实例

    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
    • time.After函数,表示多少时间,写入当前时间,在取出channel时间之前不会阻塞,后续程序可以继续执行

    • time.After函数,通常用于处理程序超时问题

    • 等待一段时间d后,Golang会发送当前时间到返回的通道上。

    • 底层的定时器不会被GC回收,如果考虑效率,可使用NewTimer创建定时器,如果不需要,则调用Timer.Stop

  • 源码实例

    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
    • 等待一段时间d后,Golang会在自己的协程中调用f。并返回一个定时器,可以使用Stop方法取消调用
  • 代码实例

    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()
    }

断续器

  • 断续器(滴答器)持有一个通道,该通道每隔一段时间发送时钟的滴答

  • 注1:从已经关闭的断续器中读取数据发生报错。所以在退出处理断续器流程前,需要先取消断续器。

  • 注2:经过取消的断续器,不能再复用,需要重新创建一个新的断续器。

  • 结构定义如下:

    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

  • 参考链接:https://blog.csdn.net/yjp19871013/article/details/82048944

  • 代码如下:

    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

  • 参考链接:https://www.kancloud.cn/digest/batu-go/153534

  • 代码如下:

    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

Golang定时器断续器的更多相关文章

  1. [Go] golang定时器与redis结合

    golang定时器与redis结合,每隔1秒ping一下,每隔20秒llen一下队列的长度 package main import ( "fmt" "time" ...

  2. [Go] golang定时器的使用

    golang中的定时器是使用的chanel阻塞来实现的,主要使用到了time包中的内容,如果有多个定时器的channel,为了防止阻塞,可以使用select来获取遍历channel 定时器获取的cha ...

  3. JS 定时器/延时器

    定时器 创建定时器    window.setInterval(方法类型,间隔时间(1000=1秒)) var timer=window.setInterval(func,2000); var i=0 ...

  4. golang 定时器

    上网查了下相关资料,基本上都介绍的是github.com\robfig\cron这个包来执行定时任务,试了下确实可以执行.但是此包下没有删 除任务的方法,只有暂停的方法(Stop),若要停止之前的任务 ...

  5. 一些css3的特效 javascript的window对象 定时器 延时器等ing...

    风车转动代码 <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> < ...

  6. JS延时器 定时器 暂停器 中断器

    // numberMillis 毫秒 function sleep(numberMillis) { var now = new Date(); var exitTime = now.getTime() ...

  7. [Golang]-7 定时器和打点器

    目录 定时器 打点器 After()方法 我们常常需要在未来某个时刻运行 Go 代码,或者在某段时间间隔内重复运行. Go 的内置 定时器 和 打点器 特性让这些很容易实现. 定时器 type Tim ...

  8. 谈谈Golang中goroutine的调度问题

    goroutine的调度问题,同样也是我之前面试的问题,不过这个问题我当时并不是很清楚,回来以后立马查阅资料,现整理出来备忘. 有一些预备知识需要说明,就是操作系统中的线程.操作系统中的线程分为两种: ...

  9. Go_20: Golang 中 time 包的使用

    time包中包括两类时间:时间点(某一时刻)和时常(某一段时间) 1. 时间常量(时间格式化) const ( ANSIC = "Mon Jan _2 15:04:05 2006" ...

随机推荐

  1. css 盒模型 文档流 几种清除浮动的方法

    盒模型 1.box-sizing: content-box 是普通的默认的一种盒子表现模式 盒子大小为 width + padding + border   content-box:此值为其默认值,其 ...

  2. ECMAScript 6简介

    一.起步 1.扎实的HTML/CSS/Javascript基本功,这是前置条件. 2.不要用任何的构建项目工具,只用最简单的<script>,把教程里的例子模仿一遍,理解用法.不推荐上来就 ...

  3. socket programming

  4. wxpython 按钮等事件的触发

    1.按钮事件的触发 方法中第二个参数为event

  5. spark1.统计句子中特定内容

    val logFile = "./README.md" // Should be some file on your server. val conf = new SparkCon ...

  6. SqlServer存储过程示例

    /* 步骤1 删除本地及海关单证待分派表.报关单表中的数据 delete from W_DOCUMENTS; delete from W_DOCUMENTS_TEST; delete from W_D ...

  7. Windows 2012R2远程桌面服务简介

    一.远程桌面服务概述 远程桌面服务加快并扩展了到任何设备的桌面和应用程序部署,在帮助保护关键知识产权的安全的同时提高了工作人员的工作效率,简化了法规遵从性. 远程桌面服务启用虚拟机基础结构 (VDI) ...

  8. 安装OUD报错,unsupported classversion 51.0

    查找,错误代码,在一篇博客中看到:补充:后经实例证明,在eclipse中进行开发的时候,build path 中JDK进行类库的编译(就是你使用类在不在这个JDK中),java compiler co ...

  9. Oracle服务端及客户端搭建帮助文档

    Oracle服务端及客户端搭建帮助文档 目录 简介 Oracle服务端安装 Oracle客户端安装 PLSQL安装 登录测试 系统配置修改 用户操作 解锁账户.密码 创建账户及密码 配置监听文件 监听 ...

  10. python入门22 pymssql模块(python连接sql server查询)

    安装 pip install pymssql 连接数据库 pymssql.connect() # coding:utf-8 import pymssql server = '192.168.8.1' ...