欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go語言實現定時器的原理及使用詳解

 更新時間:2022年12月20日 10:54:58   作者:yuzhang_zy  
這篇文章主要為大家詳細介紹了Go語言實現定時器的兩種方法:一次性定時器(Timer)和周期性定時器(Ticker),感興趣的小伙伴可以跟隨小編一起學習一下

0. 前言

在進行并發(fā)編程時,有時候會需要定時功能,比如監(jiān)控某個GO程是否會運行過長時間、定時打印日志等等。

GO標準庫中的定時器主要有兩種,一種為Timer定時器,一種為Ticker定時器。Timer計時器使用一次后,就失效了,需要Reset()才能再次生效。而Ticker計時器會一直生效,接下來分別對兩種進行介紹。

1. Timer定時器

首先介紹一下GO定時器的實現原理。

在一個GO進程中,其中的所有計時器都是由一個運行著 timerproc() 函數的 goroutine 來保護。它使用時間堆(最小堆)的算法來保護所有的 Timer,其底層的數據結構基于數組的最小堆,堆頂的元素是間隔超時最近的 Timer,這個 goroutine 會定期 wake up,讀取堆頂的 Timer,執(zhí)行對應的 f 函數或者 sendtime()函數(下文會對這兩個函數進行介紹),而后將其從堆頂移除。

接著看看Timer的結構:

type Timer struct {
    C <-chan Time
    // contains filtered or unexported fields
}

Timer中對外暴露的只有一個channel,這個 channel 也是定時器的核心。當計時結束時,Timer會發(fā)送值到channel中,外部環(huán)境在這個 channel 收到值的時候,就代表計時器超時了,可與select搭配執(zhí)行一些超時邏輯??梢酝ㄟ^time.NewTimer、time.AfterFunc或者 time.Afte對一個Timer進行創(chuàng)建。

1.1 time.NewTimer() 和 time.After()

1.1.1 time.NewTimer()

查看以下簡單的應用代碼:

package main

import (
   "fmt"
   "time"
)
type H struct {
   t *time.Timer
}

func main()  {
   fmt.Println("main")
   h:=H{t: time.NewTimer(1*time.Second)}
   go h.timer()
   time.Sleep(10*time.Second)
}

func (h *H) timer()  {
   for  {
      select {
      case <-h.t.C:
         fmt.Println("timer")
      }
   }

}

我們創(chuàng)建了一個timer,設置時間為1S。然后使用一個select對timer的C進行接受,GO程運行timer()函數,會一直阻塞直到超時發(fā)生(接收到C的數據),此時打印timer。

Stop() 停止 Timer

func (t *Timer) Stop() bool

Stop() 是 Timer 的一個方法,調用 Stop()方法,會停止這個 Timer 的計時,使其失效,之后觸發(fā)定時事件。

實際上,調用此方法后,此Timer會被從時間堆中移除。

Reset()重置Timer

注意,Timer定時器超時一次后就不會再次運行,所以需要調用Reset函數進行重置。修改select中代碼,在Case中添加一個重置的代碼:

select {
case <-h.t.C:
   fmt.Println("timer")
   h.t.Reset(1*time.Second)
}

可以看到,會不停的打印timer,這是因為使用了Reset函數重置定時器。

注意!不能隨意的對Reset方法進行調用,官網文檔中特意強調:

For a Timer created with NewTimer, Reset should be invoked only on stopped or expired timers with drained channels.

大概的意思就是說,除非Timer已經被停止或者超時了,否則不要調用Reset方法,因為,如果這個 Timer 還沒超時,不先去Stop它,而是直接Reset,那么舊的 Timer 仍然存在,并且仍可能會觸發(fā),會產生一些意料之外的事。所以通常使用如下的代碼,安全的重置一個不知狀態(tài)的Timer(以上的代碼中,Reset調用時,總是處于超時狀態(tài)):

if !t.Stop() {
    select {
    case <-h.t.C: 
    default:
    }
}
h.t.Reset(1*time.Second)

1.1.2 time.After()

此方法就像是一個極簡版的Timer使用,調用time.After(),會直接返回一個channel,當超時后,此channel會接受到一個值,簡單使用如下:

package main

import (
	"fmt"
	"time"
)

func main() {
	fmt.Println("main")
	go ticker()
	time.Sleep(100 * time.Second)
}

func ticker() {
	for {
		select {
		case <-time.After(1 * time.Second):
			fmt.Println("timer")
		}
	}
}

注意,此方法雖然簡單,但是沒有Reset方法來重置定時器,但是可以搭配for 和select的重復調用來模擬重置。

1.1.3 sendtime函數

NewTimer和After這兩種創(chuàng)建方法,會Timer在超時后,執(zhí)行一個標準庫中內置的函數:sendTime,來將當前的時間發(fā)送到channel中。

1.2 time.AfterFunc

此方法可以接受一個func類型參數,在計時結束后,會運行此函數,查看以下代碼,猜猜會出現什么結果?

package main

import (
   "fmt"
   "time"
)

func main() {
   fmt.Println("main")
   t := time.AfterFunc(1*time.Second, func() {
      fmt.Println("timer")
   })
   go timer(t)
   time.Sleep(10 * time.Second)
}
func timer(t *time.Timer) {
   select {
   case <-t.C:
      fmt.Println("123")
   }
}

結果只打印了main以及timer。這是因為此方法并不會調用上文提到的sendtime()函數,即不會發(fā)送值給Timer的Channel,所以select就會一直阻塞。

f函數

特意將AfterFunc和以上的NewTimer和After,就是因為f函數的存在。這種方式創(chuàng)建的Timer,在到達超時時間后會在單獨的goroutine里執(zhí)行函數f,而不會執(zhí)行sendtime函數。

注意,外部傳入的f參數并非直接運行在timerproc中,而是啟動了一個新的goroutine去執(zhí)行此方法。

2. Ticker定時器

Ticker定時器可以周期性地不斷地觸發(fā)時間事件,不需要額外的Reset操作。

其使用方法與Timer大同小異。通過time.NewTicker對Ticker進行創(chuàng)建,簡單的使用如下:

package main

import (
   "fmt"
   "time"
)

func main() {
   fmt.Println("main")
   t:=time.NewTicker(1*time.Second)
   go timer(t)
   time.Sleep(10 * time.Second)
}
func timer(t *time.Ticker) {
   for{
      select {
      case <-t.C:
         fmt.Println("timer")
      }
   }
}

到此這篇關于Go語言實現定時器的原理及使用詳解的文章就介紹到這了,更多相關Go語言定時器內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Golang實現短網址/短鏈服務的開發(fā)筆記分享

    Golang實現短網址/短鏈服務的開發(fā)筆記分享

    這篇文章主要為大家詳細介紹了如何使用Golang實現短網址/短鏈服務,文中的示例代碼講解詳細,具有一定的學習價值,感興趣的小伙伴可以了解一下
    2023-05-05
  • GO語言 復合類型專題

    GO語言 復合類型專題

    這篇文章主要介紹了GO語言 復合類型的的相關資料,文中講解非常細致,代碼幫助大家更好的理解和學習,感興趣的朋友可以了解下
    2020-06-06
  • 簡單聊聊為什么說Go語言字符串是不可變的

    簡單聊聊為什么說Go語言字符串是不可變的

    最近有讀者留言說,平時在寫代碼的過程中,是會對字符串進行修改的,但網上都說 Go 語言字符串是不可變的,這是為什么呢,本文就來和大家簡單講講
    2023-05-05
  • Go語言的常量、枚舉、作用域示例詳解

    Go語言的常量、枚舉、作用域示例詳解

    這篇文章主要介紹了Go語言的常量、枚舉、作用域,接下來,我們將詳細了解 Go 的變量作用域規(guī)則以及這些規(guī)則如何影響代碼編寫,需要的朋友可以參考下
    2024-07-07
  • 詳解Go語言的context包從放棄到入門

    詳解Go語言的context包從放棄到入門

    這篇文章主要介紹了Go語言的context包從放棄到入門,本文通過實例演示給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-12-12
  • golang值接收者和指針接收者的區(qū)別介紹

    golang值接收者和指針接收者的區(qū)別介紹

    這篇文章主要介紹了golang值接收者和指針接收者的區(qū)別,它和函數的區(qū)別在于方法有一個接收者,給一個函數添加一個接收者,那么它就變成了方法,接收者可以是值接收者,也可以是指針接收者,本文通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-08-08
  • 三種Golang數組拷貝方式及性能分析詳解

    三種Golang數組拷貝方式及性能分析詳解

    在Go語言中,我們可以使用for、append()和copy()進行數組拷貝。這篇文章主要為大家詳細介紹一下這三種方式的具體實現與性能分析,需要的可以參考一下
    2022-08-08
  • 基于Go?goroutine實現一個簡單的聊天服務

    基于Go?goroutine實現一個簡單的聊天服務

    對于聊天服務,想必大家都不會陌生,因為在我們的生活中經常會用到,本文我們用?Go?并發(fā)來實現一個聊天服務器,這個程序可以讓一些用戶通過服務器向其它所有用戶廣播文本消息,文中通過代碼示例介紹的非常詳細,需要的朋友可以參考下
    2023-06-06
  • golang中import cycle not allowed解決的一種思路

    golang中import cycle not allowed解決的一種思路

    這篇文章主要給大家介紹了關于golang中import cycle not allowed解決的一種思路,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面隨著小編來一起學習學習吧
    2018-08-08
  • go build和go install的區(qū)別介紹

    go build和go install的區(qū)別介紹

    這篇文章主要介紹了go build和go install的區(qū)別介紹,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12

最新評論