網站首頁 編程語言 正文
0. 前言
在進行并發編程時,有時候會需要定時功能,比如監控某個GO程是否會運行過長時間、定時打印日志等等。
GO標準庫中的定時器主要有兩種,一種為Timer定時器,一種為Ticker定時器。Timer計時器使用一次后,就失效了,需要Reset()才能再次生效。而Ticker計時器會一直生效,接下來分別對兩種進行介紹。
1. Timer定時器
首先介紹一下GO定時器的實現原理。
在一個GO進程中,其中的所有計時器都是由一個運行著 timerproc() 函數的 goroutine 來保護。它使用時間堆(最小堆)的算法來保護所有的 Timer,其底層的數據結構基于數組的最小堆,堆頂的元素是間隔超時最近的 Timer,這個 goroutine 會定期 wake up,讀取堆頂的 Timer,執行對應的 f 函數或者 sendtime()函數(下文會對這兩個函數進行介紹),而后將其從堆頂移除。
接著看看Timer的結構:
type Timer struct { C <-chan Time // contains filtered or unexported fields }
Timer中對外暴露的只有一個channel,這個 channel 也是定時器的核心。當計時結束時,Timer會發送值到channel中,外部環境在這個 channel 收到值的時候,就代表計時器超時了,可與select搭配執行一些超時邏輯。可以通過time.NewTimer、time.AfterFunc或者 time.Afte對一個Timer進行創建。
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") } } }
我們創建了一個timer,設置時間為1S。然后使用一個select對timer的C進行接受,GO程運行timer()函數,會一直阻塞直到超時發生(接收到C的數據),此時打印timer。
Stop() 停止 Timer
func (t *Timer) Stop() bool
Stop() 是 Timer 的一個方法,調用 Stop()方法,會停止這個 Timer 的計時,使其失效,之后觸發定時事件。
實際上,調用此方法后,此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 仍然存在,并且仍可能會觸發,會產生一些意料之外的事。所以通常使用如下的代碼,安全的重置一個不知狀態的Timer(以上的代碼中,Reset調用時,總是處于超時狀態):
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這兩種創建方法,會Timer在超時后,執行一個標準庫中內置的函數:sendTime,來將當前的時間發送到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()函數,即不會發送值給Timer的Channel,所以select就會一直阻塞。
f函數
特意將AfterFunc和以上的NewTimer和After,就是因為f函數的存在。這種方式創建的Timer,在到達超時時間后會在單獨的goroutine里執行函數f,而不會執行sendtime函數。
注意,外部傳入的f參數并非直接運行在timerproc中,而是啟動了一個新的goroutine去執行此方法。
2. Ticker定時器
Ticker定時器可以周期性地不斷地觸發時間事件,不需要額外的Reset操作。
其使用方法與Timer大同小異。通過time.NewTicker對Ticker進行創建,簡單的使用如下:
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") } } }
原文鏈接:https://blog.csdn.net/qq_39445165/article/details/124285999
相關推薦
- 2022-07-09 Python?操作?Excel?之?openpyxl?模塊_python
- 2022-07-27 使用Docker將容器打成鏡像的方法步驟_docker
- 2022-01-20 console基礎用法,輸出添加css,打印時間差,計數等
- 2022-03-15 命令行工具,VO和命令行之間的轉化工具
- 2022-05-02 Entity?Framework常用查詢語句_實用技巧
- 2023-07-07 關于啟動報錯ModuleNotFoundError:No module named ‘python-
- 2023-02-12 Jupyter?notebook如何實現打開數據集_python
- 2022-05-07 Python?識別錄音并轉為文字的實現_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支