Go-源码分析与tick实现定时任务

tick实现定时任务

  1. 示例代码

    package main
    
    import (
    	"fmt"
    	"math/rand"
    	"time"
    )
    
    // schedule task
    func tack(ticker <-chan time.Time) {
        // for 循环中不断执行select
    	for {
    		var flag = 0
    		select {
    		case <-ticker:
    			flag += rand.Intn(1000)
    			fmt.Printf("**********task<%d>**********\n", flag)
    		default:
    			time.Sleep(1)
    		}
    	}
    }
    
    // fake competitor
    func competitor() {
    	for {
    		fmt.Printf("competitor running...\n")
    		// goroutine 是抢占式的,必须显式地让出CPU资源去处理其它任务
    		time.Sleep(time.Millisecond)
    	}
    }
    
    // runner
    func run() {
    	// for {}
    	time.Sleep(time.Second * 5)
    }
    
    func main() {
        // Tick 返回一个长度为1的channel
    	ticker := time.Tick(time.Second * 2)
    	go tack(ticker)
    	go competitor()
    	run()
    }
    
  2. 源码分析

    // 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		// 👈 看这里,返回一个通道
    }
    
    // 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 with a period specified by the duration argument.
    // It adjusts the intervals or drops 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
    }
    
    // 👇 定时时间到将会往c中发送一个时间对象,task中从管道接手到值后就可执行响应case下的逻辑
    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:
    	}
    }
    
posted @ 2020-08-07 09:33  燕云十八骑_Z  阅读(192)  评论(0编辑  收藏  举报