掌握 Go 的計時器

發表於2023-09-27

簡介

定時器是任何程式語言的重要工具,它允許開發人員在特定時間間隔安排任務或執行程式碼。在 Go 中,定時器是透過 time 包實現的,該包提供了一系列功能來建立、啟動、停止和有效處理定時器。我們將探索 Go 中定時器的強大功能,並透過程式碼示例演示如何在應用程式中使用定時器。

建立計時器

要在 Go 中建立一個定時器,我們可以使用 time.NewTimer() 函式,該函式將持續時間作為引數。下面是一個示例:

func CreateTimer() {
    timer := time.NewTimer(2 * time.Second)
    fmt.Println("Timer created.")

    <-timer.C // 阻塞
    fmt.Println("Timer expired.")
}

在上述程式碼片段中,我們使用 time.NewTimer() 建立了一個持續時間為 2 秒的新定時器。<-timer.C 語句會阻塞執行,直到定時器過期。定時器到期後,"Timer expired."(定時器已過期)資訊將列印到控制檯。

停止計時器

在某些情況下,您可能想在定時器到期前停止它。為此,您可以使用定時器物件的 Stop() 方法。讓我們修改之前的示例,加入定時器停止功能:

func StopTimer() {
    timer := time.NewTimer(2 * time.Second)
    fmt.Println("Timer created.")

    go func() {
        <-timer.C
        fmt.Println("Timer expired.")
    }()

    time.Sleep(1 * time.Second)
    stopped := timer.Stop()
    if stopped {
        fmt.Println("Timer stopped.")
    } else {
        fmt.Println("Timer has already expired.")
    }
}

在更新後的程式碼中,我們建立了一個 goroutine 來處理定時器過期,這樣就可以在定時器過期前停止它。我們使用 time.Sleep() 函式來模擬在嘗試停止計時器之前正在進行的一些工作。最後,我們呼叫 timer.Stop() 停止定時器。如果定時器已過期,timer.Stop() 返回 false,並列印 "定時器已過期"。否則,我們將列印 "定時器已停止"。

重置計時器

Go 還提供了重置活動定時器的方法。透過 Reset() 方法,您可以更改活動定時器的持續時間,重新開始倒數計時。下面是一個示例:

func ResetTimer() {
    timer := time.NewTimer(10 * time.Second)
    fmt.Printf("time: %d, Timer created.\n", time.Now().Unix())

    time.Sleep(2 * time.Second)
    reset := timer.Reset(3 * time.Second)
    if reset {
        fmt.Printf("time: %d, Timer reset.\n", time.Now().Unix())
    } else {
        fmt.Printf("time: %d, Timer has already expired.\n", time.Now().Unix())
    }
    <-timer.C // 阻塞
    fmt.Printf("time: %d, Timer expired again.\n", time.Now().Unix())
}

輸出為:

time: 1695183503, Timer created.
time: 1695183505, Timer reset.
time: 1695183508, Timer expired again.

在上述程式碼中,我們建立了一個持續時間為 10 秒的計時器。使用 time.Sleep() 等待 2 秒後,我們呼叫 timer.Reset(),新的持續時間為 3 秒。如果定時器尚未過期,則重置操作成功,我們將列印 "定時器重置"。否則,進入到 <-timer.C 阻塞階段,然後列印 我們將列印 "Timer expired again."。

重置定時器與停止定時器

瞭解重置定時器和使用 Stop() 停止定時器之間的區別非常重要。

func CompareResetAndStop() {
    timer := time.NewTimer(5 * time.Second)
    fmt.Printf("time: %d, Timer created.\n", time.Now().Unix())

    go func() {
        <-timer.C
        fmt.Printf("time: %d, Timer expired.\n", time.Now().Unix())
    }()

    time.Sleep(2 * time.Second)
    timer.Reset(3 * time.Second)
    fmt.Printf("time: %d, Timer reset.\n", time.Now().Unix())

    time.Sleep(2 * time.Second)
    timer.Stop()
    fmt.Printf("time: %d, Timer stopped.\n", time.Now().Unix())
}

輸出為:

time: 1695183802, Timer created.
time: 1695183804, Timer reset.
time: 1695183806, Timer stopped.

在本例中,我們建立了一個持續時間為 5 秒的計時器。2 秒後,我們使用 timer.Reset() 將計時器重置為 3 秒。之後,再過 2 秒,我們使用 timer.Stop() 停止計時器。重置定時器會改變其持續時間並重新開始倒數計時,而停止定時器則會立即停止執行,無論剩餘持續時間多長。

帶 Ticker 的計時器

Go 提供了一種 Ticker 型別,它是一種專門的定時器,可在指定的時間間隔內重複觸發。定時器可用於定期執行任務。

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

    go func() {
        for range ticker.C {
            fmt.Printf("time: %d, Ticker ticked!\n", time.Now().Unix())
        }
    }()

    time.Sleep(5 * time.Second)
}

在本例中,我們使用 time.NewTicker() 建立了一個持續時間為 1 秒的 Ticker。然後,我們啟動一個 goroutine,從 ticker.C channel 接收值,每當滴答聲響起時,goroutine 就會發出一個值。在 goroutine 中,每次接收到一個 tick 時,我們都會列印 "Ticker ticked!"。呼叫 time.Sleep() 可以讓滴答滴答執行 5 秒鐘,然後退出程式。

使用 Select 的超時

Go 的 select 語句允許在多個通道上執行非阻塞操作。這可以用來使用計時器實現超時。

func TimeOut() {
    ch := make(chan string)

    go func() {
        time.Sleep(2 * time.Second)
        ch <- "Operation completed."
    }()

    select {
    case msg := <-ch:
        fmt.Println(msg)
    case <-time.After(1 * time.Second):
        fmt.Println("Timeout reached.")
    }
}

在本例中,我們建立了一個 channel ch,並啟動一個 goroutine 來模擬耗時 2 秒的操作。我們使用 select 語句從 ch 接收資訊,或使用 time.After() 等待超時。如果操作在 1 秒內完成,則列印訊息。否則,將執行超時情況,並列印 "Timeout reached."。

本文由mdnice多平臺釋出

相關文章