日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

Go?Ticker?周期性定時器用法及實現原理詳解_Golang

作者:yi個俗人 ? 更新時間: 2022-10-21 編程語言

前言

定時器在Go語言應用中使用非常廣泛,Go語言的標準庫里提供兩種類型的計時器,一種是一次性的定時器Timer,另外一種是周期性的定時器Ticker。本文主要來看一下Ticker的用法和實現原理,需要的朋友可以參考以下內容,希望對大家有幫助。

Ticker

Ticker是周期性定時器,即周期性的觸發一個事件,它會以一個間隔(interval)往channel發送一個事件(當前時間),而channel的接收者可以以固定的時間間隔從channel中讀取事件。通過Ticker本身提供的管道將事件傳遞出去。

應用示例

package main
import (
	"fmt"
	"time"
)
func main()  {
	ticker := time.NewTicker(time.Second * 1) //創建一個周期性定時器
	i := 1
	for  {
		fmt.Println(i, "====>", <-ticker.C)
		if i == 5 {
			ticker.Stop() //停止定時器
			break
		}
		i++
	}
}

輸出結果:

1 ====> 2022-08-24 15:58:38.971837 +0800 CST m=+1.001366085
2 ====> 2022-08-24 15:58:39.971154 +0800 CST m=+2.000695418
3 ====> 2022-08-24 15:58:40.971633 +0800 CST m=+3.001185460
4 ====> 2022-08-24 15:58:41.97109 +0800 CST m=+4.000654126
5 ====> 2022-08-24 15:58:42.971594 +0800 CST m=+5.001169210

創建定時器

使用NewTicker()方法就可以創建一個周期性定時器,函數如下:

func NewTicker(d Duration) *Ticker

其中參數d即為定時器時間觸發的周期,為一個時間段。

停止定時器

使用定時器對外暴露的 Stop 方法就可以停掉一個周期性定時器, 函數如下:

func (t *Ticker) Stop()

該方法會停止計時,停止后不會向定時器的管道中寫入事件,但管道并不會被關閉。

管道在使用完成后,生命周期結束后會自動釋放。

實現原理

數據結構

Ticker的數據結構與Timer完全一致:

通過src/time.sleep.go:Ticker定義了Timer數據結構:

type Ticker struct {
    C <-chan Time
    r runtimeTimer
}

它提供了一個channel,在定時時間到達之前,沒有數據寫入Ticker.C會一直阻塞,直到時間到達,向channel寫入系統時間,阻塞解除,可以從中讀取數據,這就是一個事件。

我們可以理解為Ticker.C即面向Ticker用戶的,Ticker.r是面向底層的定時器實現。

runtimeTimer

runtimeTimer與Timer一樣,任務的載體,用于監控定時任務,每創建一個Timer就創建一個runtimeTimer變量,然后把它交給系統進行監控,我們通過設置runtimeTimer過期后的行為來達到定時的目的。

源碼包src/time/sleep.go:runtimeTimer定義了其數據結構:

type runtimeTimer struct {
    tb uintptr                          // 存儲當前定時器的數組地址
    i  int                              // 存儲當前定時器的數組下標
    when   int64                        // 當前定時器觸發時間
    period int64                        // 當前定時器周期觸發間隔
    f      func(interface{}, uintptr)   // 定時器觸發時執行的函數
    arg    interface{}                  // 定時器觸發時執行函數傳遞的參數一
    seq    uintptr                      // 定時器觸發時執行函數傳遞的參數二(該參數只在網絡收發場景下使用)
}

創建Ticker

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), // Ticker跟Timer的重要區就是提供了period這個參數,據此決定timer是一次性的,還是周期性的
            f:      sendTime,
            arg:    c,
        },
    }
    startTimer(&t.r)
    return t
}

NewTicker()構造了一個Ticker,然后把Ticker.r通過startTimer()交給系統協程維護。

其中period為事件觸發的周期。

停止Ticker

停止Ticker,只是把Ticker從系統協程中移除。

func (t *Ticker) Stop() {
    stopTimer(&t.r)
}

stopTicker()即通知系統協程把該Ticker移除,即不再監控。系統協程只是移除Ticker并不會關閉管道,以避免用戶協程讀取錯誤。

注意:

Ticker在使用完后務必要釋放,否則會產生資源泄露,進而會持續消耗CPU資源,最后會把CPU耗盡。

ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()

Ticker 與 Timer 區別

  • Ticker —— 重復性執行任務非常有用呢
  • Timer —— 用于執行一次性任務

小結

Ticker相關內容總結如下:

  • 使用time.NewTicker()來創建一個定時器;
  • 使用Stop()來停止一個定時器;
  • 定時器使用完畢要釋放,否則會產生資源泄露;


原文鏈接:https://juejin.cn/post/7135368112584687623

欄目分類
最近更新