golang 定時器的不同任務

CHHC發表於2024-11-27

應用場景

電池船資料上報頻次:航行中 1次/30秒,不航行 1次/1小時

電池簇資料上報頻次:工作中 1次/1秒,不工作 不上報

main.go

package main

import (
	"fmt"
	"os"
	"os/signal"
	"syscall"
	"ticker/util"
	"time"
)

var ticker1 *util.DynamicTicker
var ticker2 *util.DynamicTicker

func report_batteryship() {
	// 航行時1秒/次,不航行時10秒/次
	fmt.Println("report_batteryship:", time.Now())
}

func report_batterycluster() {
	// 工作時1秒/次,不工作時不上報
	fmt.Println("report_batterycluster:", time.Now())
}

func main() {
	sleeptime := 10 * time.Second

	// ticker1 = util.NewDynamicTicker(1*time.Second, report_batteryship)
	// ticker1.SetWorkStatus(true)

	// go func() {
	// 	fmt.Println("航行中")

	// 	time.Sleep(sleeptime)

	// 	fmt.Println("不航行")
	// 	ticker1.ChangeInterval(10 * time.Second)

	// 	time.Sleep(sleeptime * 6)

	// 	fmt.Println("航行中")
	// 	ticker1.ChangeInterval(1 * time.Second)
	// }()

	ticker2 = util.NewDynamicTicker(2*time.Second, report_batterycluster)
	ticker2.SetWorkStatus(true)

	go func() {
		fmt.Println("開始工作")

		time.Sleep(sleeptime)

		fmt.Println("停止工作")
		ticker2.SetWorkStatus(false)

		time.Sleep(sleeptime)

		fmt.Println("開始工作")
		ticker2.SetWorkStatus(true)
	}()

	// 監聽作業系統訊號,阻塞直到接收到訊號
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	<-quit
}

  

ticker.go

package util

import (
    "sync"
    "time"
)

// 是一個可以動態改變時間間隔的定時器
type DynamicTicker struct {
    currentTicker *time.Ticker   // 當前正在使用的 ticker
    nextTicker    *time.Ticker   // 下一個要切換到的 ticker
    work          bool           // 工作狀態標誌
    quit          chan struct{}  // 用於停止 goroutine 的訊號通道
    mu            sync.Mutex     // 互斥鎖,用於保護併發訪問
    worker        func()         // 定時執行的工作函式
    wg            sync.WaitGroup // 等待組,用於等待 goroutine 結束
}

// 建立一個新的 DynamicTicker 例項
func NewDynamicTicker(interval time.Duration, worker func()) *DynamicTicker {
    dt := &DynamicTicker{
        currentTicker: time.NewTicker(interval), // 初始化當前 ticker
        quit:          make(chan struct{}),      // 初始化停止訊號通道
        worker:        worker,                   // 設定工作函式
    }
    dt.wg.Add(1) // 增加等待組的計數器
    go dt.run()  // 啟動 goroutine 執行 run 方法
    return dt
}

// 監聽訊號
func (dt *DynamicTicker) run() {
    defer dt.wg.Done() // 在 goroutine 結束時減少等待組的計數器
    for {
        select {
        case <-dt.currentTicker.C: // 接收到當前 ticker 的訊號
            dt.mu.Lock()
            if dt.work { // 如果處於工作狀態
                dt.worker() // 執行工作函式
            }
            // 如果存在下一個 ticker,則切換到下一個 ticker
            if dt.nextTicker != nil {
                dt.currentTicker.Stop()          // 停止當前 ticker
                dt.currentTicker = dt.nextTicker // 切換到下一個 ticker
                dt.nextTicker = nil              // 重置下一個 ticker
            }
            dt.mu.Unlock()
        case <-dt.quit: // 接收到停止訊號
            dt.currentTicker.Stop()   // 停止當前 ticker
            if dt.nextTicker != nil { // 如果存在下一個 ticker,也停止它
                dt.nextTicker.Stop()
            }
            return // 退出 goroutine
        }
    }
}

// 動態改變定時器的間隔
func (dt *DynamicTicker) ChangeInterval(newInterval time.Duration) {
    dt.mu.Lock()
    defer dt.mu.Unlock()
    dt.nextTicker = time.NewTicker(newInterval) // 建立新的 ticker 作為下一個 ticker
}

// 設定定時器的工作狀態
func (dt *DynamicTicker) SetWorkStatus(work bool) {
    dt.mu.Lock()
    defer dt.mu.Unlock()
    dt.work = work // 更新工作狀態
}

// 停止定時器
func (dt *DynamicTicker) Stop() {
    close(dt.quit) // 關閉停止訊號通道
    dt.wg.Wait()   // 等待 goroutine 結束
}

相關文章