Golang当中的定时器

2023-11-19

前言

在平时写代码的时候,我们经常会遇到在将来某个时间点或者间隔一段时间重复执行函数。这个时候我们就可以考虑使用定时器。本片文章主要介绍一下golang当中的几个常用的定时器。time.Timer,time.Ticker,time.After以及time.AfterFunctime.Ticker的基本使用

定时器的基本使用

golang当中的定时器有这个一次性的定时器(Timer)和周期性的定时器(Ticker).在平时的编程当中经常会使用timer当中的ticker,AfterFunc定时器,而NewTicker是每隔多长时间触发,NewTimer是等待多长时间触发一次请注意是只触发一次。请注意一下两者的区别。
下面我们来首先来使用一下这两个定时器首先是这个Timer定时器

package main

import (
	"fmt"
	"time"
)
func main() {
	myTimer := time.NewTimer(time.Second * 3) //初始化定时器
	var i = 0
	for {
		select {
		case <-myTimer.C:
			i++
			fmt.Printf("the counter is%d", i)
			myTimer.Reset(time.Second * 3) //注意需要重新设置
		}
	}
	myTimer.Stop() //不在使用需要将其停止
}

注意这个timer定时器超时之后需要重新进行设置,才能重新触发。如果上面的代码我们没有Reset,那么就会导致死锁。其实我们也可以看看这个Timer是怎么是实现的

func NewTimer(d Duration) *Timer {
    c := make(chan Time, 1)
    t := &Timer{
        C: c,  // 信道
        r: runtimeTimer{
            when: when(d),  // 触发时间
            f:    sendTime, // 时间到了之后的调用函数
            arg:  c,        // 调用sendTime时的入参
        },
    }
    startTimer(&t.r)  // 把定时器的r字段放入由定时器维护协程维护的堆中
    return t
} 

从上面的构造函数中可以大概看出定时器的工作流程,这里面最重要的是runtimeTimer。构造定时器的时候会把runtimeTimer放入由定时器维护协程维护的堆中,当时间到了之后,维护协程把r从堆中移除,并调用r的sendTime函数,sendTime的入参是定时器的信道C。可以推断,sendTime中执行的逻辑应该是向信道C中推送时间,通知上游系统时间到了,而事实正是如此:

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

其实这个time.After就是对这个time.Timer的一个封装,所以如果我们上面使用这个time.After那么会频繁的创建time.Timer对象
下面我们在来看一下这个time.AterFunc()定时器。
Golang当中的AfterFunc函数用于等待经过时间,此后在其自己的协程当中调用定义的函数f.函数在时间包下定义。下面我们一起看看如何使用这个

import (
	"fmt"
	"time"
)

func main() {
	f := func() {
		fmt.Println("the func is call after 3 second")
	}
	myTime := time.AfterFunc(time.Second*3, f)

	defer myTime.Stop() //定时器不用了需要关闭
	time.Sleep(time.Second * 4)
}

下面我们在看看这个time.NewTicker定时器的使用

package main

import (
	"fmt"
	"time"
)

func main() {
	mytick := time.NewTicker(time.Second * 2)
	defer mytick.Stop() //定时器不用了需要关闭
	done := make(chan struct{})
	go func() {
		for {
			time.Sleep(time.Second * 10)
			done <- struct{}{}
		}
	}()
	for {
		select {
		case <-done:
			fmt.Println("done!!!!")
			return
		case t := <-mytick.C:
			fmt.Printf("the curtime is %v\n", t)
		}
	}

}

下面我们来看一下这个陷阱,这个需要注意

package main

import (
	"fmt"
	"time"
)
func main() {
	var count int
	for {
		select {
		case <-time.Tick(time.Second * 1):
			fmt.Println("case1")
			count++
			fmt.Println("count--->", count)
		case <-time.Tick(time.Second * 2):
			fmt.Println("case2")
			count++
			fmt.Println("count--->", count)
		}
	}
	
}

这个代码是有陷阱的,下面我们来看看这个运行结果是什么?
在这里插入图片描述
可见 case2 永远没有被执行到,问题就出在代码逻辑上,首先看time.Tick方法。我们可以看一下这个方法就知道

func Tick(d Duration) <-chan Time {
	if d <= 0 {
		return nil
	}
	return NewTicker(d).C
}

它每次都会创建一个新的定时器,随着 for 循环进行, select 始终监听两个新创建的定时器,老的定时器被抛弃掉了,也就不会去读取老定时器中的通道。
select 可以同时监听多个通道,谁先到达就先读取谁,如果同时有多个通道有消息到达,那么会随机读取一个通道,其他的通道由于没有被读取,所以数据不会丢失,需要循环调用 select 来读取剩下的通道。

总结:

  • tick创建完成之后,不是马上有一个tick.第一个tick在你设置的多少秒之后才会进行创建
  • golang当中的定时器实质上是这个单项的管道
  • time.NewTicker会定时触发任务,当下一次执行到来而当前任务画面执行完,会等待当前任务执行完毕在进行下一次任务。
  • Ticker和Timer的不同之处是,Ticker时间到达之后不需要人为的调用Reset方法来重新设置时间
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Golang当中的定时器 的相关文章

随机推荐

  • 阅读笔记篇卷首语

    2023年8月31日 周四晚上 我决定开设这个专栏 因为我知道有些文章可以改变人生 值得去细读
  • 【unity3D】创建TextMeshPro(TMP)中文字体(解决输入中文乱码问题)

    未来的游戏开发程序媛 现在的努力学习菜鸡 本专栏是我关于游戏开发的学习笔记 本篇是unity的TMP中文输入显示乱码的解决方式 创建 TextMeshPro 中文字体 遇到的问题描述 解决方式 Font Asset Creator 面板扩展
  • linux基本命令大全

    基本命令 关机 shutdown h halt init 0 poweroff 重启 shutdown r reboot init 6 pwd 查看工作目录 ls 查看指定目录的内容 l 列表显示 a 显示所有 包括隐藏文件 h 人性化的显
  • 【QT学习笔记】QAction和QToolButton的使用

    QAction可以在QT Creator中Action Editor中创建 QAction创建之后的两个使用方式 放到tool bar中 跟QToolButton绑定 ui gt tBtnListIni gt setDefaultActio
  • 虚拟服务器如何传东西,虚拟服务器如何传东西

    虚拟服务器如何传东西 内容精选 换一换 华为云帮助中心 为用户提供产品简介 价格说明 购买指南 用户指南 API参考 最佳实践 常见问题 视频帮助等技术文档 帮助您快速上手使用华为云服务 计费项包括存储费和流量费 存储费根据存储库的不同进行
  • Reinforcement Learning 强化学习(四)

    Task03 本次学习主要参照Datawhale开源学习及强化学习蘑菇书Easy RL 第4章 策略梯度 Policy Gradient 4 1 策略梯度算法 在强化学习中有 3 个组成部分 演员 actor 环境 environment
  • odoo tree form 视图禁止创建、修改、删除、复制

  • 用标准C语言初始化线性表,c语言实现线性表的初始化,创建,查找,删除

    1 第一步定义线性表结构 typedef struct ElementType data MaxSize int length Lineartable 2 第二步线性表初始化 初始化线性表 Lineartable INITAL Linear
  • Go 每日一库之 gjson

    快速使用 先安装 go get github com tidwall gjson 后使用 package main import fmt github com tidwall gjson func main json name first
  • Hackinglab(鹰眼)——解密关

    目录 1 以管理员身份登录系统 2 邂逅对门的妹纸 3 万恶的Cisco 4 万恶的加密 5 喜欢泡网吧的小明 6 异常数据 7 md5真的能碰撞嘛 8 小明爱上了一个搞硬件的小姑凉 9 有签名限制的读取任意文件 10 美丽的邂逅与密码器的
  • 什么是卷积

    什么是卷积 卷积 convolution 是一种运算 你可以类比于加 减 乘 除 矩阵的点乘与叉乘等等 它有自己的运算规则 卷积的符号是星号 表达式为 连续的为 f g
  • 小程序web-view打开PDF格式文件的安卓苹果兼容性问题

    小程序中打开pdf格式原本可以使用web view 承载网页的容器 会自动铺满整个小程序页面 个人类型的小程序暂不支持使用
  • 《小岛区块链》之区块链起源

    今天我们开始 小岛区块链 的第三章节 智能合约 本文衔接于 小岛区块链 第二章节 共识之后 为方便大家理解 请先阅读 小岛区块链 之起源 小岛区块链 之共识 一 选举记账还能再升级一下吗 上回说到 在小郑的提议下 鹿谷的村民们都开始按照投票
  • 2019年电赛之路——2015年电子设计竞赛A题任务设计

    参加19年电赛 我们奔着电源题来的 所以我们一开始要练习的题目就选定了15年的A题 因为我们找到了几个国一方案 但是只有一个方案 代码和PCB图都没有 这也是我们失误的地方 本来的路线应该是找一个成品方案 模仿着做下来 先做下来一个题目后
  • 剑指 Offer 05. 替换空格(java+python)

    请实现一个函数 把字符串 s 中的每个空格替换成 20 示例 1 输入 s We are happy 输出 We 20are 20happy 限制 0 lt s 的长度 lt 10000 java StringBuilder StringB
  • tomcat7启动报taglib标签错误

    问题描述 应用在tomcat6上发布没有问题 部署到tomcat7后报错 不识别配置的taglib标签 问题截图如下 解决方法 查询应用环境 除服务器为tomcat7外 配置的web xml 头文件为 测试头文件为
  • Arduino 自动初始化ESP8266为透传模式

    通过上篇可以把esp8266设置成透传模式 但掉电后esp8266会退出透传模式 需要重新初始化 这样arduino和esp8266结合使用时 每次重启后都要通过电脑重新设置esp8266进入透传模式 这里通过把AT指令写进arduino程
  • elasticsearch 为“非查询字段”不建索引 index store

    官方文档 index 简章翻译 文末附原文 索引index 这个参数可以控制字段应该怎样建索引 怎样查询 它有以下三个可用值 no 不把此字段添加到索引中 也就是不建索引 此字段不可查询 not analyzed 将字段的原始值放入索引中
  • python元组练习题

    Python 元组综合练习 使用python语言创建空元组 score 按学号顺序 由小到大 保存多个学生一门课程的 考试成绩 调用元组操作的常用函数实现以下功能 1 创建score 元组 其中包含10 个数值 68 87 92 100 7
  • Golang当中的定时器

    定时器 前言 定时器的基本使用 前言 在平时写代码的时候 我们经常会遇到在将来某个时间点或者间隔一段时间重复执行函数 这个时候我们就可以考虑使用定时器 本片文章主要介绍一下golang当中的几个常用的定时器 time Timer time