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

詳解Golang中NewTimer計時器的底層實現(xiàn)原理

 更新時間:2023年05月04日 10:48:49   作者:starrySky  
本文將主要介紹一下Go語言中的NewTimer,首先展示基于NewTimer創(chuàng)建的定時器來實現(xiàn)超時控制。接著通過一系列問題的跟進,展示了NewTimer的底層實現(xiàn)原理,需要的可以參考一下

1.簡介

本文將介紹 Go 語言中的NewTimer,首先展示基于NewTimer創(chuàng)建的定時器來實現(xiàn)超時控制。接著通過一系列問題的跟進,展示了NewTimer的底層實現(xiàn)原理。

2.基本使用

我們首先通過一個簡單的例子,來展示是怎么基于NewTimer實現(xiàn)超時控制的。

假設有一個需求,要求在 5 秒鐘內(nèi)完成某個任務,否則就認為任務失敗。這時我們就可以使用 NewTimer 來實現(xiàn)超時控制。具體的實現(xiàn)步驟如下:

  • 首先,基于NewTimer創(chuàng)建一個 Timer 對象,設定超時時間為 5 秒鐘。
  • 然后,啟動一個 goroutine 來執(zhí)行任務,并在任務完成后向通道發(fā)送一個完成信號。
  • 最后,使用 select 語句等待 Timer 的到期事件或任務完成信號,如果 Timer 先到期,就認為任務超時了,否則就認為任務完成了。

下面是一個簡單的實現(xiàn)代碼展示:

package main

import (
        "fmt"
        "time"
)

func main() {
        // 創(chuàng)建一個定時器,超時時間為 5 秒鐘
        timer := time.NewTimer(5 * time.Second)

        // 啟動一個 goroutine 執(zhí)行任務,并在任務完成后向通道發(fā)送一個完成信號
        done := make(chan bool, 1)
        go func() {
                // 模擬任務執(zhí)行耗時
                time.Sleep(2 * time.Second)
                done <- true
        }()

        // 等待任務完成或者超時
        select {
        case <-done:
                // 任務完成,輸出完成信息
                fmt.Println("Task finished.")
        case <-timer.C:
                // 超時,輸出超時信息
               fmt.Println("Task timed out.")
        }
}

在上述代碼中,我們首先使用NewTimer創(chuàng)建了一個time.Timer對象,超時時間為 5 秒鐘。然后啟動一個 goroutine 來執(zhí)行任務,并在任務完成后向通道 done 發(fā)送一個完成信號。最后使用 select 語句等待任務完成信號或者Timer的到期事件,如果Timer先到期,就認為任務超時了,否則就認為任務完成了。 在運行上述代碼時,我們可以看到,在任務完成前 5 秒鐘內(nèi),程序輸出如下信息:

Task finished.

如果將任務完成時間改為超過 5 秒鐘,程序將會在 5 秒鐘后超時,輸出如下信息:

Task timed out.

通過這個簡單的例子,我們可以看到,如果任務在指定超時時間內(nèi)完成,此時會執(zhí)行正常的業(yè)務邏輯;如果任務未在指定的超時時間內(nèi)完成,此時將走執(zhí)行超時邏輯。

通過上述程序演示,我們展示了如何使用NewTimer創(chuàng)建的 time.Timer 實現(xiàn)超時控制的基本方法。

3.實現(xiàn)原理

3.1 內(nèi)容分析

回顧上面的示例代碼,我們實現(xiàn)超時控制的主要機制,是通過select語句同時監(jiān)聽兩個channel,一個是任務執(zhí)行狀態(tài)的channel,一個是定時器的channel

當任務執(zhí)行完成時,便通過channel對主協(xié)程進行通知。當定時器到達我們指定的時間,也就是超時時間,此時也通過定時器的channel進行通知。

 同時監(jiān)聽這兩個channel,如果任務先執(zhí)行完成,此時將會走select語句中正常業(yè)務邏輯case的代碼,如果是在到達預定時間,任務仍沒有完成,此時通過定時器channel進行通知,從而走超時業(yè)務邏輯case的代碼,從而實現(xiàn)超時控制。

因此,這里主要的問題是,是如何在到達超時時間時,準時往定時器中的C對應的channel發(fā)送送數(shù)據(jù),從而來告知其他協(xié)程,已經(jīng)到達超時時間了呢,這個是如何做到的呢?

3.2 基本思路

下面先來看看NewTimer方法返回Timer結構體的內(nèi)容,定義如下:

type Timer struct {
   C <-chan Time
   r runtimeTimer
}

可以看到,Timer結構體中C是一個chan Time類型的變量。在前面的代碼的例子中,select語句是監(jiān)聽Timer結構體中的C變量,從中來讀取數(shù)據(jù)的。

那么,當?shù)竭_超時時間時,TimerC對應的channel將會有數(shù)據(jù)到達,那么肯定有其他地方,在到達超時時間時,會往Timer中的C發(fā)送數(shù)據(jù)。

那么現(xiàn)在的主要問題,是怎么做到當?shù)竭_指定時間時,往Timer中的C發(fā)送數(shù)據(jù)呢?

其實,在go語言中,存在這樣一個運行時函數(shù)startTimer ,定義如下:

func startTimer(*runtimeTimer)

它的作用是啟動一個定時器,當定時器到期時,會執(zhí)行相應的回調(diào)函數(shù)并傳遞回調(diào)參數(shù)。在 startTimer 函數(shù)內(nèi)部,會使用系統(tǒng)調(diào)用來啟動一個底層的操作系統(tǒng)定時器,等到定時器超時時,底層系統(tǒng)會自動觸發(fā)一個信號(例如 Unix 平臺上的 SIGALRM 信號),然后該信號將由 Go 運行時內(nèi)部的信號處理函數(shù)捕獲,并最終調(diào)用相關的回調(diào)函數(shù)。

那么,這里我們似乎可以使用startTimer來實現(xiàn),當?shù)竭_指定時間時,往channel發(fā)送數(shù)據(jù),從而達到通知其他協(xié)程的效果。

3.3 實現(xiàn)步驟

首先,我們已經(jīng)知道,startTimer能夠啟動一個定時器,當定時器到期時,會執(zhí)行相應的回調(diào)函數(shù)并傳遞回調(diào)參數(shù)。而定時器的到期時間、回調(diào)函數(shù)以及回調(diào)函數(shù)的參數(shù),則是通過runtimeTimer結構體傳遞過去的。

下面我們只需要runtimeTimer字段的含義,然后根據(jù)其含義,正確設置runtimeTimer結構體字段,調(diào)用startTimer方法啟動一個定時器,就能夠實現(xiàn)在指定時間時,執(zhí)行某段邏輯。下面我們來看看runtimeTime的定義:

type runtimeTimer struct {
   pp       uintptr
   when     int64
   period   int64
   f        func(any, uintptr) // NOTE: must not be closure
   arg      any
   seq      uintptr
   nextwhen int64
   status   uint32
}

下面對runtimeTimer中的字段進行說明:

  • when int64:表示定時器應該在何時觸發(fā)。該值的單位是納秒
  • period int64:表示定時器的重復周期。如果定時器不需要重復觸發(fā),則該值為 0
  • f func(any, uintptr):指向定時器到期后需要執(zhí)行的函數(shù)。
  • arg any:表示傳遞給定時器到期后執(zhí)行的函數(shù)的參數(shù),將傳遞給f的第一個參數(shù)
  • nextwhen int64:如果定時器是重復觸發(fā)的,則 nextwhen 表示下一次觸發(fā)的時間。
  • seq uintptr:用于防止定時器被錯誤的重置。當定時器被重置時,seq 會被更新。

基于對上面字段含義的理解,此時我們定義一個runtimeTimer結構體,然后調(diào)用startTimer,從而來實現(xiàn)能夠在指定的某個時間點,往某個channel發(fā)送數(shù)據(jù)。具體實現(xiàn)如下:

// 定義一個channel,用于發(fā)送數(shù)據(jù)
c := make(chan Time, 1)
r := runtimeTimer{
      // 指定超時時間戳
      when: when(d),
      // 指定回調(diào)函數(shù)
      f: func sendTime(c any, seq uintptr) {
               select {
               case c.(chan Time) <- Now():
               default:
              }
        },
      // 傳遞給回調(diào)函數(shù)的參數(shù)
      arg:  c,
   },
}
// 調(diào)用startTimer啟動一個定時器
startTimer(&t.r)

首先會創(chuàng)建一個帶有緩沖的通道 c。

接著初始化runtimeTimer結構體的值,設定好超時時間,回調(diào)函數(shù)以及參數(shù)。超時時間使用的是when函數(shù)來獲取計數(shù)器的結束時間。when 函數(shù)會根據(jù)給定的時間間隔 d,返回一個絕對時間點,即計時器結束時間。

f字段指定的回調(diào)函數(shù),則是將當前時間 Now() 發(fā)送到通道 c 中。當?shù)竭_指定超時時間時,其將會調(diào)用回調(diào)函數(shù)f,同時將runtimeTimer結構體中arg字段的值,作為參數(shù)傳遞到回調(diào)函數(shù)當中。

然后調(diào)用startTimer啟動一個定時器。當?shù)竭_超時時間,將會調(diào)用回調(diào)函數(shù),回調(diào)函數(shù)其會往一開始定義的channel發(fā)送數(shù)據(jù)。

至此,我們實現(xiàn)了最開始提到的,當?shù)竭_指定時間時,往Timer中的C發(fā)送數(shù)據(jù)這個任務。

3.4 NewTimer的實現(xiàn)

回到我們的題目,NewTimer計時器的底層實現(xiàn)原理是什么?事實上,NewTimer創(chuàng)建的定時器,也確實是基于startTimer來實現(xiàn)的,下面我們來看看其實現(xiàn):

func NewTimer(d Duration) *Timer {
   c := make(chan Time, 1)
   t := &Timer{
      C: c,
      r: runtimeTimer{
         when: when(d),
         f:    sendTime,
         arg:  c,
      },
   }
   startTimer(&t.r)
   return t
}

首先會創(chuàng)建一個帶有緩沖的通道 c。然后創(chuàng)建一個 Timer 對象 t,將 c 通道賦值給 tC 屬性。channel之后將作為回調(diào)函數(shù)的參數(shù),同時也會作為Timer對象中C屬性的值。這樣子回調(diào)函數(shù)和Timer結構體中C變量與回調(diào)函數(shù)的channel事實上是共用一個channel的。

runtimeTimer結構體中的回調(diào)函數(shù)sendTime的實現(xiàn)與之前講述的并無差異,都是將當前時間 Now() 發(fā)送到通道中,這里將不再贅述。

Timer結構體中C變量與回調(diào)函數(shù)的channel事實上是共用一個channel的,當?shù)竭_超時時間,則會執(zhí)行回調(diào)函數(shù),往channel發(fā)送數(shù)據(jù)。而通過select語句對Timer中的channel進行監(jiān)聽的協(xié)程,此時也正常接收到通知了。

4.總結

在這篇文章中,我們首先展示了使用NewTimer創(chuàng)建一個定時器,然后基于此實現(xiàn)超時控制。我們發(fā)現(xiàn),比較重要的一個機制是,timer對象是在到達超時時間時,timer對象中C對應的channel將會接收到數(shù)據(jù)。

timer對象在達到超時時間時,C屬性對應的channel便能夠接收到數(shù)據(jù),這個是怎么做到的呢? 我們基于此進行了分析,事實上,首先是基于startTimer創(chuàng)建定時器,同時runtimeTimer結構體指定了回調(diào)函數(shù),從而實現(xiàn)達到超時時間時,回調(diào)函數(shù)往C屬性對應的channel發(fā)送數(shù)據(jù)。然后就能夠基于該特性,能夠實現(xiàn)超時控制等機制。

到此這篇關于詳解Golang中NewTimer計時器的底層實現(xiàn)原理的文章就介紹到這了,更多相關Golang NewTimer計時器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Go語言中println和fmt.Println區(qū)別

    Go語言中println和fmt.Println區(qū)別

    本文主要介紹了Go語言中println和fmt.Println區(qū)別,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-07-07
  • 詳解Go語言中的逃逸分析

    詳解Go語言中的逃逸分析

    逃逸分析是編譯器用于決定將變量分配到棧上還是堆上的一種行為,下面小編就來為大家詳細講講go語言中是如何進行逃逸分析的,需要的小伙伴可以參考下
    2023-09-09
  • 詳解Golang time包中的time.Duration類型

    詳解Golang time包中的time.Duration類型

    在日常開發(fā)過程中,會頻繁遇到對時間進行操作的場景,使用 Golang 中的 time 包可以很方便地實現(xiàn)對時間的相關操作,本文講解一下 time 包中的 time.Duration 類型,需要的朋友可以參考下
    2023-07-07
  • 淺談Golang內(nèi)存逃逸

    淺談Golang內(nèi)存逃逸

    本文主要介紹了Golang內(nèi)存逃逸,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-08-08
  • 詳解Go hash算法的支持

    詳解Go hash算法的支持

    這篇文章主要介紹了詳解Go hash算法的支持,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-09-09
  • Golang實現(xiàn)支持多種類型的set

    Golang實現(xiàn)支持多種類型的set

    在項目開發(fā)中,常常會用到set去重,為什么不寫一個set呢,而且go現(xiàn)在支持了泛型,所以本文就來用Golang實現(xiàn)一個支持多種類型的set呢
    2023-05-05
  • Go語言之自定義集合Set

    Go語言之自定義集合Set

    本文主要介紹的是Go語言的自定義集合Set,文中介紹的很詳細,有需要的可以參考學習。
    2016-08-08
  • Go中的go.mod使用詳解

    Go中的go.mod使用詳解

    這篇文章主要介紹了Go中的go.mod使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • Go語言:打造優(yōu)雅數(shù)據(jù)庫單元測試的實戰(zhàn)指南

    Go語言:打造優(yōu)雅數(shù)據(jù)庫單元測試的實戰(zhàn)指南

    Go語言數(shù)據(jù)庫單元測試入門:聚焦高效、可靠的數(shù)據(jù)庫代碼驗證!想要確保您的Go應用數(shù)據(jù)層堅如磐石嗎?本指南將手把手教您如何利用Go進行數(shù)據(jù)庫單元測試,輕松揪出隱藏的bug,打造無懈可擊的數(shù)據(jù)處理邏輯,一起來探索吧!
    2024-01-01
  • 詳解如何在Go服務中做鏈路追蹤

    詳解如何在Go服務中做鏈路追蹤

    使用 Go 語言開發(fā)微服務的時候,需要追蹤每一個請求的訪問鏈路,本文主要介紹了如何在Go 服務中做鏈路追蹤,感興趣的可以了解一下
    2021-09-09

最新評論