golang定時器案例詳解
一、什么是 Go 語言中的定時器?
基本概念
- 定時器(Timer):就像一次性鬧鐘,設置后在指定時間觸發(fā)一次事件,適合延遲執(zhí)行或超時控制。
- 計數(shù)器(Ticker):像每小時響一次的鐘,定期重復觸發(fā)事件,適合周期性任務。
Go 語言中的實現(xiàn)
Go 的 time 包提供了兩個核心工具:
- time.Timer:用于一次性定時任務。
- time.Ticker:用于周期性任務。
二、定時器的產(chǎn)生背景
最原始的問題:程序需要“等待一段時間”
在計算機早期(單線程執(zhí)行時代),程序的執(zhí)行是連續(xù)的。
但是很多場景需要“延時執(zhí)行”或“定時觸發(fā)”:
- 例 如:
- 網(wǎng)絡請求超時控制(5 秒無響應就退出)
- 每隔一段時間采集一次數(shù)據(jù)
- 定時清理緩存或 session
- 周期性健康檢查(health check)
- 執(zhí)行計劃任務(類似 cron job)
這些任務都需要一種機制:“在未來的某個時間點執(zhí)行某個動作”。
三、Go 定時器的內(nèi)部實現(xiàn)原理
Go runtime 的定時器系統(tǒng)是由調(diào)度器(scheduler)管理的。
主要基于 最小堆(min-heap)結構。
思想:
- 所有活躍的
Timer、Ticker都存儲在一個堆中; - 堆頂是下一個即將觸發(fā)的定時器;
- runtime 中有一個專門的 goroutine(叫 timerproc)循環(huán)運行:
- 檢查堆頂?shù)牡狡跁r間;
- 睡眠到該時間;
- 時間一到,就喚醒并執(zhí)行對應的回調(diào)或發(fā)送信號。
偽代碼示意:
for {
t := heap.Top() // 最近的定時器
sleepUntil(t.when)
trigger(t) // 執(zhí)行任務
}這種機制的好處:
- 所有定時任務集中管理;
- 效率極高(對堆的操作,O(log n) 插入、O(1) 取最早任務);
四、time.Timer—— 一次性定時器
1. 基本用法
package main
import (
"fmt"
"time"
)
func main() {
timer := time.NewTimer(2 * time.Second)
fmt.Println("等待中...")
<-timer.C // 阻塞,直到2秒后收到信號
fmt.Println("2秒到了!")
}解釋:
time.NewTimer(d)創(chuàng)建一個定時器;timer.C是一個只讀 channel;- 當時間到達后,系統(tǒng)會向
timer.C發(fā)送一個時間值; <-timer.C會阻塞直到時間到。
2. 另一種方式:time.After
time.After 是一個簡化版本,會在指定時間后返回一個 channel:
<-time.After(3 * time.Second) fmt.Println("3秒到了!")這其實相當于:
timer := time.NewTimer(3 * time.Second) <-timer.C
適合簡單一次性延遲的場景。
3.取消定時器:Stop()
如果不再需要一個定時器(例如提前退出、釋放資源),應該調(diào)用:
timer := time.NewTimer(5 * time.Second)
go func() {
<-timer.C
fmt.Println("定時任務執(zhí)行")
}()
time.Sleep(2 * time.Second)
if timer.Stop() {
fmt.Println("定時器被取消")
} 注意:如果定時器已經(jīng)觸發(fā)(channel 已經(jīng)有值),Stop() 會返回 false。
4.重置定時器:Reset()
如果想復用一個定時器:
timer := time.NewTimer(2 * time.Second)
<-timer.C // 第一次觸發(fā)
timer.Reset(1 * time.Second) // 重新開始計時
<-timer.C
fmt.Println("又過了1秒")五、time.Ticker—— 周期性定時器
1.開啟定時器。
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
for t := range ticker.C {
fmt.Println("時間到了:", t)
}每隔 1 秒,ticker.C 就會收到一個時間值。
2.停止定時器
必須手動調(diào)用:
ticker.Stop()
否則 goroutine 不會結束,可能導致內(nèi)存泄漏。
六、定時器案例
1.用timer模擬鬧鐘貪睡
package main
import (
"fmt"
"time"
)
func main() {
// 鬧鐘,自動重試任務
// NewTimer 創(chuàng)建一個 Timer,它會在最少過去時間段 d 后到期,向其自身的 C 字段發(fā)送當時的時間
timer1 := time.NewTimer(5 * time.Second)
fmt.Println("開始時間:", time.Now().Format("2006-01-02 15:04:05"))
go func(t *time.Timer) {
times := 0
for {
<-t.C
fmt.Println("timer", time.Now().Format("2006-01-02 15:04:05"))
// 從t.C中獲取數(shù)據(jù),此時time.Timer定時器結束。如果想再次調(diào)用定時器,只能通過調(diào)用 Reset() 函數(shù)來執(zhí)行
// Reset 使 t 重新開始計時,(本方法返回后再)等待時間段 d 過去后到期。
// 如果調(diào)用時 t 還在等待中會返回真;如果 t已經(jīng)到期或者被停止了會返回假。
times++
// 調(diào)用 reset 重發(fā)數(shù)據(jù)到chan C
fmt.Println("調(diào)用 reset 重新設置一次timer定時器,并將時間修改為2秒")
t.Reset(2 * time.Second)
if times > 3 {
fmt.Println("調(diào)用 stop 停止定時器")
t.Stop()
}
}
}(timer1)
time.Sleep(30 * time.Second)
fmt.Println("結束時間:", time.Now().Format("2006-01-02 15:04:05"))
fmt.Println("ok")
}2.用ticker模擬心跳檢測
package main
import (
"fmt"
"time"
)
func main() {
ticker := time.NewTicker(2 * time.Second)
defer ticker.Stop() // 函數(shù)結束時關閉 ticker
done := make(chan bool)
// 模擬外部協(xié)程:10秒后關閉心跳
go func() {
time.Sleep(10 * time.Second)
done <- true
}()
for {
select {
case t := <-ticker.C:
fmt.Println("發(fā)送心跳包:", t)
case <-done:
fmt.Println("停止發(fā)送心跳包")
return
}
}
}我們可以通過以上案例看到,timer定時器和reset函數(shù)組合,也可以達到ticker的效果。
七、總結
- 對比
- Timer:一次性觸發(fā),適合延遲任務或超時控制。
- Ticker:周期性觸發(fā),適合重復任務。
- 選擇場景
- Timer:如 HTTP 請求超時、延遲執(zhí)行任務。
- Ticker:如定時刷新數(shù)據(jù)、心跳檢測。
- 注意事項
- Ticker 如果不停止,會持續(xù)運行,可能導致資源泄露。始終記得在不需要時調(diào)用 Stop()。
- Timer 觸發(fā)后會自動失效,無需擔心資源問題。
到此這篇關于golang定時器案例的文章就介紹到這了,更多相關golang定時器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
pytorch中的transforms.ToTensor和transforms.Normalize的實現(xiàn)
本文主要介紹了pytorch中的transforms.ToTensor和transforms.Normalize的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-04-04

