[云原生] Go 定时器

  • Timer:时间到了,执行只执行1次
  • Ticker:时间到了,多次执行

Timer

  • 基本使用

:::details

go 复制代码
func main() {
	/**
	* 1. 创建定时器
	**/
	/**
	NewTimer creates a new Timer that will send the current time on its channel
	after at least duration d
	*/
	log.Println(time.Now())
	timer := time.NewTimer(2 * time.Second)
	fmt.Println(<-timer.C)
	fmt.Printf("%T : %v \r\n", timer, timer)
	log.Println(time.Now())
}

输出

shell 复制代码
2022/05/17 14:16:23 2022-05-17 14:16:23.9679315 +0800 CST m=+0.001999701
2022-05-17 14:16:25.9945183 +0800 CST m=+2.028580001
*time.Timer : &{0xc000048060 {0 1053841046621200 0 0x9c6520 0xc000048060 0 0 0}} 
2022/05/17 14:16:25 2022-05-17 14:16:25.9953948 +0800 CST m=+2.029456501

:::

  • timer只能用一次

:::details

go 复制代码
func main() {
	timer := time.NewTimer(time.Second)

	fmt.Println(<-timer.C)
	fmt.Println("时间到")

	// fmt.Println(<-timer.C)

	fmt.Println("main exit")
}

输出

shell 复制代码
2022-05-17 14:17:32.8901259 +0800 CST m=+1.014617701
时间到
main exit

把注释打开再运行的结果

shell 复制代码
2022-05-17 14:18:28.2956576 +0800 CST m=+1.006551901
时间到
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
        one.go:14 +0xfa
exit status 2

:::

  • 延时

:::details

go 复制代码
func main() {
	// 1. sleep
	log.Println("sleep", time.Now())
	time.Sleep(2 * time.Second)
	log.Println("--------sleep-------")
	// 2. <- time.C
	log.Println("<-time.C", time.Now())
	timer := time.NewTimer(2 * time.Second)
	<-timer.C
	log.Println("-------- <-timer.C -------")
	// 3. <- time.After
	log.Println("<- time.After", time.Now())
	<-time.After(2 * time.Second)
	log.Println("--------after-------")
}

输出

shell 复制代码
2022/05/17 14:19:21 sleep 2022-05-17 14:19:21.6913174 +0800 CST m=+0.002678801
2022/05/17 14:19:23 --------sleep-------
2022/05/17 14:19:23 <-time.C 2022-05-17 14:19:23.7048594 +0800 CST m=+2.016214301
2022/05/17 14:19:25 -------- <-timer.C -------
2022/05/17 14:19:25 <- time.After 2022-05-17 14:19:25.7141672 +0800 CST m=+4.025515701
2022/05/17 14:19:27 --------after-------

:::

  • Reset

:::details

go 复制代码
func main() {
	// 5.重置定时器
	timer5 := time.NewTimer(3 * time.Second)
	timer5.Reset(10 * time.Second)
	fmt.Println(time.Now())
	fmt.Println(<-timer5.C)
}

输出

shell 复制代码
2022-05-17 14:20:40.9176901 +0800 CST m=+0.002613601
2022-05-17 14:20:50.928108 +0800 CST m=+10.013010401

:::

  • Stop

:::details

go 复制代码
func main() {
	// 4.停止定时器
	timer4 := time.NewTimer(2 * time.Second)
	go func() {
		fmt.Println("定时器开始执行")
		<-timer4.C
		fmt.Println("定时器执行完毕")
	}()
	b := timer4.Stop()
	if b {
		fmt.Println("timer4已经关闭")
	}
}

输出

shell 复制代码
timer4已经关闭

:::

Ticker

  • 跟timer的用法差不多

:::details

go 复制代码
package main

import (
	"fmt"
	"time"
)

func main() {
	// 1.获取ticker对象
	ticker := time.NewTicker(2 * time.Second)
	i := 0
	// 子协程
	go func() {
		for {
			//<-ticker.C
			i++
			fmt.Println(<-ticker.C)
			if i == 5 {
				//停止
				ticker.Stop()
			}
		}
	}()
	fmt.Println("main should exit, but sleep")
	time.Sleep(20 * time.Second)
}

输出

shell 复制代码
main should exit, but sleep
2022-05-17 14:21:43.6481633 +0800 CST m=+2.009463901
2022-05-17 14:21:45.6518969 +0800 CST m=+4.013193301
2022-05-17 14:21:47.6512444 +0800 CST m=+6.012536601
2022-05-17 14:21:49.6423737 +0800 CST m=+8.003661701
2022-05-17 14:21:51.656046 +0800 CST m=+10.017329801

:::

定时器的源码

:::tip

  • 环境
go 复制代码
> go version
go version go1.17.7 windows/amd64

:::

timer

:::details

  • 部分其他包的函数
  • runtimeNano : runtimeNano returns the current value of the runtime clock in nanoseconds.
go 复制代码
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package time

// Sleep pauses the current goroutine for at least the duration d.
// A negative or zero duration causes Sleep to return immediately.
func Sleep(d Duration)

// Interface to timers implemented in package runtime.
// Must be in sync with ../runtime/time.go:/^type timer
type runtimeTimer struct {
	pp       uintptr
	when     int64
	period   int64
	f        func(interface{}, uintptr) // NOTE: must not be closure
	arg      interface{}
	seq      uintptr
	nextwhen int64
	status   uint32
}

// when is a helper function for setting the 'when' field of a runtimeTimer.
// It returns what the time will be, in nanoseconds, Duration d in the future.
// If d is negative, it is ignored. If the returned value would be less than
// zero because of an overflow, MaxInt64 is returned.
func when(d Duration) int64 {
	if d <= 0 {
		return runtimeNano()
	}
	t := runtimeNano() + int64(d)
	if t < 0 {
		// N.B. runtimeNano() and d are always positive, so addition
		// (including overflow) will never result in t == 0.
		t = 1<<63 - 1 // math.MaxInt64
	}
	return t
}

func startTimer(*runtimeTimer)
func stopTimer(*runtimeTimer) bool
func resetTimer(*runtimeTimer, int64) bool
func modTimer(t *runtimeTimer, when, period int64, f func(interface{}, uintptr), arg interface{}, seq uintptr)

// The Timer type represents a single event.
// When the Timer expires, the current time will be sent on C,
// unless the Timer was created by AfterFunc.
// A Timer must be created with NewTimer or AfterFunc.
type Timer struct {
	C <-chan Time
	r runtimeTimer
}

// Stop prevents the Timer from firing.
// It returns true if the call stops the timer, false if the timer has already expired or been stopped.
// Stop does not close the channel, to prevent a read from the channel succeeding incorrectly.
//
// To ensure the channel is empty after a call to Stop, check the
// return value and drain the channel.
// For example, assuming the program has not received from t.C already:
//
// 	if !t.Stop() {
// 		<-t.C
// 	}
//
// This cannot be done concurrent to other receives from the Timer's channel or other calls to the Timer's Stop method.
//
// For a timer created with AfterFunc(d, f), if t.Stop returns false, then the timer
// has already expired and the function f has been started in its own goroutine;
// Stop does not wait for f to complete before returning.
// If the caller needs to know whether f is completed, it must coordinate
// with f explicitly.
func (t *Timer) Stop() bool {
	if t.r.f == nil {
		panic("time: Stop called on uninitialized Timer")
	}
	return stopTimer(&t.r)
}

// NewTimer creates a new Timer that will send
// the current time on its channel after at least duration d.
func NewTimer(d Duration) *Timer {
	c := make(chan Time, 1)
	t := &Timer{
		C: c,
		r: runtimeTimer{
			when: when(d),
			f:    sendTime,
			arg:  c,
		},
	}
	startTimer(&t.r)
	return t
}

// Reset changes the timer to expire after duration d.
// It returns true if the timer had been active, false if the timer had
// expired or been stopped.
//
// For a Timer created with NewTimer, Reset should be invoked only on
// stopped or expired timers with drained channels.
//
// If a program has already received a value from t.C, the timer is known
// to have expired and the channel drained, so t.Reset can be used directly.
// If a program has not yet received a value from t.C, however,
// the timer must be stopped and---if Stop reports that the timer expired
// before being stopped---the channel explicitly drained:
//
// 	if !t.Stop() {
// 		<-t.C
// 	}
// 	t.Reset(d)
//
// This should not be done concurrent to other receives from the Timer's
// channel.
//
// Note that it is not possible to use Reset's return value correctly, as there
// is a race condition between draining the channel and the new timer expiring.
// Reset should always be invoked on stopped or expired channels, as described above.
// The return value exists to preserve compatibility with existing programs.
//
// For a Timer created with AfterFunc(d, f), Reset either reschedules
// when f will run, in which case Reset returns true, or schedules f
// to run again, in which case it returns false.
// When Reset returns false, Reset neither waits for the prior f to
// complete before returning nor does it guarantee that the subsequent
// goroutine running f does not run concurrently with the prior
// one. If the caller needs to know whether the prior execution of
// f is completed, it must coordinate with f explicitly.
func (t *Timer) Reset(d Duration) bool {
	if t.r.f == nil {
		panic("time: Reset called on uninitialized Timer")
	}
	w := when(d)
	return resetTimer(&t.r, w)
}

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

// After waits for the duration to elapse and then sends the current time
// on the returned channel.
// It is equivalent to NewTimer(d).C.
// The underlying Timer is not recovered by the garbage collector
// until the timer fires. If efficiency is a concern, use NewTimer
// instead and call Timer.Stop if the timer is no longer needed.
func After(d Duration) <-chan Time {
	return NewTimer(d).C
}

// AfterFunc waits for the duration to elapse and then calls f
// in its own goroutine. It returns a Timer that can
// be used to cancel the call using its Stop method.
func AfterFunc(d Duration, f func()) *Timer {
	t := &Timer{
		r: runtimeTimer{
			when: when(d),
			f:    goFunc,
			arg:  f,
		},
	}
	startTimer(&t.r)
	return t
}

func goFunc(arg interface{}, seq uintptr) {
	go arg.(func())()
}

:::

ticker

:::details

go 复制代码
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package time

import "errors"

// A Ticker holds a channel that delivers ``ticks'' of a clock
// at intervals.
type Ticker struct {
	C <-chan Time // The channel on which the ticks are delivered.
	r runtimeTimer
}

// NewTicker returns a new Ticker containing a channel that will send
// the time on the channel after each tick. The period of the ticks is
// specified by the duration argument. The ticker will adjust the time
// interval or drop ticks to make up for slow receivers.
// The duration d must be greater than zero; if not, NewTicker will
// panic. Stop the ticker to release associated resources.
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
}

// Stop turns off a ticker. After Stop, no more ticks will be sent.
// Stop does not close the channel, to prevent a concurrent goroutine
// reading from the channel from seeing an erroneous "tick".
func (t *Ticker) Stop() {
	stopTimer(&t.r)
}

// Reset stops a ticker and resets its period to the specified duration.
// The next tick will arrive after the new period elapses.
func (t *Ticker) Reset(d Duration) {
	if t.r.f == nil {
		panic("time: Reset called on uninitialized Ticker")
	}
	modTimer(&t.r, when(d), int64(d), t.r.f, t.r.arg, t.r.seq)
}

// Tick is a convenience wrapper for NewTicker providing access to the ticking
// channel only. While Tick is useful for clients that have no need to shut down
// the Ticker, be aware that without a way to shut it down the underlying
// Ticker cannot be recovered by the garbage collector; it "leaks".
// Unlike NewTicker, Tick will return nil if d <= 0.
func Tick(d Duration) <-chan Time {
	if d <= 0 {
		return nil
	}
	return NewTicker(d).C
}

:::

相关推荐
学c真好玩5 分钟前
Django创建的应用目录详细解释以及如何操作数据库自动创建表
后端·python·django
Asthenia04125 分钟前
GenericObjectPool——重用你的对象
后端
Piper蛋窝15 分钟前
Go 1.18 相比 Go 1.17 有哪些值得注意的改动?
后端
excel29 分钟前
招幕技术人员
前端·javascript·后端
盖世英雄酱581361 小时前
什么是MCP
后端·程序员
淋一遍下雨天1 小时前
Spark Streaming核心编程总结(四)
java·开发语言·数据库
小白学大数据1 小时前
如何避免爬虫因Cookie过期导致登录失效
开发语言·爬虫·python·scrapy
爱吃烤鸡翅的酸菜鱼2 小时前
【SpringMVC】概念引入与连接
java·开发语言·mysql
小白学大数据2 小时前
Python自动化解决滑块验证码的最佳实践
开发语言·python·自动化