go-timer

设计原理

网络轮询器

所有的计时器都以最小四叉堆的形式**存储在处理器 runtime.p**中。

网络轮询器实际上是对 I/O 多路复用技术的封装,网络轮询器并不是由运行时中的某一个线程独立运行的,运行时的调度器和系统调用都会通过 runtime.netpoll 与网络轮询器交换消息,获取待执行的 Goroutine 列表,并将待执行的 Goroutine 加入运行队列等待处理。

所有的文件 I/O、网络 I/O 和计时器都是由网络轮询器管理的,它是 Go 语言运行时重要的组成部分

在这里插入图片描述

处理器P与定时器相关的字段

type p struct {
	...
	timersLock mutex
	timers []*timer

	numTimers     uint32
	adjustTimers  uint32
	deletedTimers uint32
	...
}
  • timersLock:定时器互斥锁
  • timers:最小四叉堆
  • numTimers:处理器P中的定时器数量
  • adjustTimers:处理器中处于 timerModifiedEarlier 状态的计时器数量;
  • deletedTimers:处理器中处于 timerDeleted 状态的计时器数量;

timer数据结构

runtime.timer是计时器的结构体,每个timer计时器都存在最小四叉堆(runtime.p.timers)内,他的数结构体:

type timer struct {
	pp puintptr

	when     int64
	period   int64
	f        func(interface{}, uintptr)
	arg      interface{}
	seq      uintptr
	nextwhen int64
	status   uint32
}
  • when:当前计时器被唤醒的时间
  • period:两次被唤醒的间隔
  • f:每当计时器被唤醒时都会调用的函数
  • arg:f的传参
  • nextWhen:计时器处于timerModifiedXX状态时,用于设置when字段
  • status:计时器的状态

计时器对外暴露的结构体是:

type Timer struct {
	C <-chan Time
	r runtimeTimer
}
  • C:订阅g

当计时器失效时,订阅计时器 Channel 的 Goroutine 会收到计时器失效的时间。

状态机

状态解释
timerNoStatus还没有设置状态
timerWaiting等待触发
timerRunning运行计时器函数
timerDeleted被删除
timerRemoving正在被删除
timerRemoved已经被停止并从堆中删除
timerModifying正在被修改
timerModifiedEarlier被修改到了更早的时间
timerModifiedLater被修改到了更晚的时间
timerMoving已经被修改正在被移动

计时器基本使用

timer

Timer类型代表单次时间事件。当Timer到期时,当时的时间会被发送给C,除非Timer是被AfterFunc函数创建的

type Timer struct {
    C <-chan Time
    // 内含隐藏或非导出字段
}
  • func NewTimer(d Duration) *Timer:NewTimer创建一个Timer,它会在最少过去时间段d后到期,向其自身的C字段发送当时的时间。

    func Timer() {
    	t := time.NewTimer(10 * time.Second)
    	fmt.Println("start")	//马上输出start
    	fmt.Println(<-t.C)		//阻塞等待channel唤醒此G,10秒后输出当前时间
    }
    
  • func AfterFunc(d Duration, f func()) *Timer:AfterFunc另起一个go协程,等待时间段d过去,然后调用f。它返回一个Timer,可以通过调用其Stop方法来取消等待和对f的调用。

    func Timer() {
    	a := func() {
    		fmt.Println("到期")
    	}
    	time.AfterFunc(10*time.Second, a)
    	fmt.Println("start")
    
    }
    // 会打印“到期”,只要主协程没关闭
    
  • func (t *Timer) Reset(d Duration) bool:Reset使t重新开始计时,(本方法返回后再)等待时间段d过去后到期。如果调用时t还在等待中会返回真;如果t已经到期或者被停止了会返回假。

  • func (t *Timer) Stop() bool:Stop停止Timer的执行。如果停止了t会返回真;如果t已经被停止或者过期了会返回假。Stop不会关闭通道t.C,以避免从该通道的读取不正确的成功。

ticker

Ticker保管一个通道,并每隔一段时间向其传递"tick"。

type Ticker struct {
    C <-chan Time // 周期性传递时间信息的通道
    // 内含隐藏或非导出字段
}
  • func NewTicker(d Duration) *Ticker:NewTicker返回一个新的Ticker,该Ticker包含一个通道字段,并会每隔时间段d就向该通道发送当时的时间。它会调整时间间隔或者丢弃tick信息以适应反应慢的接收者。如果d<=0会panic。关闭该Ticker可以释放相关资源。

    func Ticker() {
    	t := time.NewTicker(time.Second * 5)
    	for now := range t.C {	//一直轮询读取chan,协程会被newticker网络轮询器唤醒
    		fmt.Println(now)
    	}
    }
    

time包时间戳使用

  • type Time struct:Time代表一个纳秒精度的时间点。

  • Now():Now返回当前本地时间。

  • func Parse(layout, value string) (Time, error):Parse解析一个格式化的时间字符串并返回它代表的时间。layout定义了参考时间:

    const timeFormat = "2006-01-02 15:04:05"
    if a, err := time.Parse(timeFormat, "2023-09-18 16:03:21"); err != nil {
        fmt.Println(err.Error())
    } else {
        fmt.Println(a)		//2023-09-18 16:03:21 +0000 UTC
    }
    
  • func (t Time) Date() (year int, month Month, day int):返回时间点t对应的年、月、日。

  • func (t Time) Clock() (hour, min, sec int):返回t对应的那一天的时、分、秒。

  • func (t Time) AddDate(years int, months int, days int) Time:AddDate返回增加了给出的年份、月份和天数的时间点Time。例如,时间点January 1, 2011调用AddDate(-1, 2, 3)会返回March 4, 2010。

    fmt.Println(time.Now()) //2023-09-18 16:03:21.6799254 +0800 CST m=+0.009288501	
    fmt.Println(time.Now().AddDate(-1, 2, 3))	//2022-11-21 16:03:21.6799254 +0800 CST
    
  • func (t Time) Format(layout string) string:Format根据layout指定的格式返回t代表的时间点的格式化文本表示

    const timeFormat = "2006-01-02 15:04:05"
    fmt.Println(time.Now().Format(timeFormat))	// 2022-11-21 16:53:08
    

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值