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

Go語言并發(fā)定時(shí)任務(wù)之從Sleep到Context的8種寫法全解析

 更新時(shí)間:2025年08月15日 08:19:39   作者:mCell  
這篇文章主要為大家詳細(xì)介紹了Go語言并發(fā)定時(shí)任務(wù)之從Sleep到Context的8種寫法的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下

本文從 0 開始,帶你用一個(gè)超簡(jiǎn)單的任務(wù)(每秒打印一個(gè)數(shù)字)學(xué)會(huì):

  • Go time 包的幾種定時(shí) API
  • goroutineWaitGroup、channel、select、context 的用法
  • 哪些并發(fā)寫法是坑,哪些是生產(chǎn)推薦
  • 完整可運(yùn)行的 main.go

背景

任務(wù):每秒打印一個(gè)數(shù)字,連續(xù) 10 次。

隱含要求

  • 間隔穩(wěn)定(大約 1 秒)
  • 順序正確(1 → 10)
  • 最好可以提前取消

為什么這個(gè)任務(wù)重要?

這個(gè)簡(jiǎn)單任務(wù)涵蓋了并發(fā)編程的核心要素:定時(shí)控制、順序保證生命周期管理。掌握它,你就掌握了 Go 并發(fā)的基礎(chǔ)!

1. 基礎(chǔ)寫法:time.Sleep

func UseTimeSleep() {
    for i := 1; i <= 10; i++ {
        // 暫停當(dāng)前goroutine的執(zhí)行1秒鐘
        // time.Second是預(yù)定義的Duration常量,等于1,000,000,000納秒
        time.Sleep(time.Second)

        // 打印當(dāng)前數(shù)字
        fmt.Println(i)
    }
}

關(guān)鍵 API 詳解

  • time.Sleep(d Duration):暫停當(dāng)前 goroutine 的執(zhí)行至少 d 時(shí)長(zhǎng)
  • time.Second:預(yù)定義的 Duration 常量,表示 1 秒(1e9 納秒)
  • fmt.Println:標(biāo)準(zhǔn)輸出函數(shù),線程安全

優(yōu)點(diǎn)

  • 順序正確
  • 最簡(jiǎn)單易懂

缺點(diǎn)

  • 無法中途取消
  • 實(shí)際間隔 = 1 秒 + 打印耗時(shí),會(huì)有累積誤差
  • 阻塞當(dāng)前 goroutine,無法同時(shí)執(zhí)行其他任務(wù)

適用場(chǎng)景:簡(jiǎn)單腳本、不需要取消功能的短期任務(wù)

2.time.After:一次定時(shí)一次信號(hào)

func UseTimeAfter() {
    for i := 1; i <= 10; i++ {
        // time.After返回一個(gè)單向接收通道(<-chan Time)
        // 該通道會(huì)在指定時(shí)間后發(fā)送一個(gè)時(shí)間值
        timerChannel := time.After(time.Second)

        // <-操作符會(huì)阻塞,直到通道發(fā)送值
        <-timerChannel

        fmt.Println(i)
    }
}

關(guān)鍵點(diǎn)解析

  • time.After(d Duration) <-chan Time:返回一個(gè)只接收通道
  • 通道操作<-ch阻塞操作,直到有數(shù)據(jù)可讀
  • 每次循環(huán)都創(chuàng)建新 Timer 對(duì)象,有 GC 壓力

潛在問題

  • 頻繁創(chuàng)建 Timer 對(duì)象,可能引起 GC 壓力
  • 無法復(fù)用定時(shí)器
  • 同樣無法取消

適用場(chǎng)景:?jiǎn)未纬瑫r(shí)控制,如網(wǎng)絡(luò)請(qǐng)求超時(shí)

3.time.NewTicker:復(fù)用定時(shí)器

func UseTimeTicker() {
    // 創(chuàng)建Ticker對(duì)象,每1秒向C通道發(fā)送當(dāng)前時(shí)間
    ticker := time.NewTicker(time.Second)

    // defer確保函數(shù)退出時(shí)停止Ticker,釋放資源
    defer ticker.Stop()

    for i := 1; i <= 10; i++ {
        // 從Ticker的C通道讀取值(阻塞直到時(shí)間到)
        <-ticker.C

        fmt.Println(i)
    }
}

Ticker 對(duì)象解析

  • time.NewTicker(d Duration) *Ticker:創(chuàng)建周期性定時(shí)器
  • ticker.C <-chan Time:定時(shí)觸發(fā)的通道
  • ticker.Stop():停止定時(shí)器,必須調(diào)用否則資源泄漏

優(yōu)點(diǎn)

  • 單一定時(shí)器復(fù)用,高效
  • 間隔精確

注意事項(xiàng)

  • 忘記 Stop()會(huì)導(dǎo)致 goroutine 泄漏
  • 使用后應(yīng)立即 defer Stop()

適用場(chǎng)景:周期性任務(wù),如定時(shí)數(shù)據(jù)采集

4. 并發(fā)誤區(qū)

4.1channel并發(fā)版(順序混亂)

func UseChannel() {
    // 創(chuàng)建無緩沖通道,發(fā)送和接收會(huì)同步阻塞
    ch := make(chan int)

    for i := 1; i <= 10; i++ {
        // 啟動(dòng)goroutine(輕量級(jí)線程)
        go func(num int) {
            // 每個(gè)goroutine等待不同時(shí)間
            time.Sleep(time.Second * time.Duration(num))

            // 向通道發(fā)送數(shù)字
            ch <- num
        }(i) // 注意:必須傳入i的副本,避免閉包捕獲問題
    }

    for i := 1; i <= 10; i++ {
        // 從通道接收值(阻塞直到有數(shù)據(jù))
        value := <-ch
        fmt.Println(value)
    }
}

執(zhí)行順序解析

問題分析

  • 10 個(gè) goroutine 并發(fā)執(zhí)行,完成順序不確定
  • 通道接收順序 = goroutine 完成順序 ≠ 數(shù)字順序
  • 間隔時(shí)間不固定(1 秒到 10 秒)

正確使用場(chǎng)景:獨(dú)立任務(wù)并行處理,如批量圖片處理

4.2WaitGroup并發(fā)版(順序混亂)

func UseGoroutine() {
    // 創(chuàng)建WaitGroup用于等待所有g(shù)oroutine完成
    var wg sync.WaitGroup

    for i := 1; i <= 10; i++ {
        // 增加等待計(jì)數(shù)
        wg.Add(1)

        go func(num int) {
            // 函數(shù)退出時(shí)減少計(jì)數(shù)
            defer wg.Done()

            time.Sleep(time.Second * time.Duration(num))
            fmt.Println(num)
        }(i)
    }

    // 阻塞直到所有g(shù)oroutine完成
    wg.Wait()
}

WaitGroup 原理

  • Add(delta int):增加等待計(jì)數(shù)
  • Done():減少計(jì)數(shù)(等價(jià)于 Add(-1))
  • Wait():阻塞直到計(jì)數(shù)歸零

問題分析

  • 輸出順序完全隨機(jī)
  • 間隔時(shí)間不固定
  • 多個(gè) goroutine 同時(shí)調(diào)用 fmt.Println 可能輸出交錯(cuò)

適用場(chǎng)景:并行執(zhí)行獨(dú)立任務(wù),不需要順序保證

5. 正確的并發(fā)寫法

5.1 單 goroutine + WaitGroup

func UseSingleGoroutine() {
    var wg sync.WaitGroup

    // 只需等待1個(gè)goroutine
    wg.Add(1)

    // 啟動(dòng)工作goroutine
    go func() {
        // 確保結(jié)束時(shí)通知WaitGroup
        defer wg.Done()

        for i := 1; i <= 10; i++ {
            fmt.Println(i)
            time.Sleep(time.Second)
        }
    }()

    // 主goroutine等待工作完成
    wg.Wait()
}

架構(gòu)解析

主goroutine         工作goroutine
    │                    │
    │── wg.Add(1) ────>?│
    │                    │
    │                    ├─ 執(zhí)行循環(huán)
    │                    │   打印+等待
    │                    │
    │?─── wg.Wait() ─────┤
    ▼                    ▼

優(yōu)點(diǎn)

  • 順序和間隔完全可控
  • 結(jié)構(gòu)清晰
  • 為添加取消功能留出空間

適用場(chǎng)景:需要順序執(zhí)行的定時(shí)任務(wù)

5.2select+Ticker

func UseSelectAndTicker() {
    ticker := time.NewTicker(time.Second)
    defer ticker.Stop()

    for i := 1; i <= 10; i++ {
        // select監(jiān)控多個(gè)通道操作
        select {
        // 當(dāng)ticker.C有值時(shí)執(zhí)行
        case <-ticker.C:
            fmt.Println(i)
        }
    }
}

select 關(guān)鍵字詳解

  • 用于監(jiān)聽多個(gè)通道操作
  • 當(dāng)任意 case 可執(zhí)行時(shí),隨機(jī)選擇一個(gè)執(zhí)行
  • 無 default 時(shí)會(huì)阻塞
  • 常用于多路復(fù)用

進(jìn)階用法(添加退出通道):

quit := make(chan struct{})
// ...
select {
case <-ticker.C:
    fmt.Println(i)
case <-quit:
    fmt.Println("提前退出")
    return
}

適用場(chǎng)景:需要監(jiān)控多個(gè)事件源的定時(shí)任務(wù)

5.3context+Ticker(生產(chǎn)推薦)

func UseContextWithTicker() {
    // 創(chuàng)建可取消的context
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel() // 確保資源釋放

    ticker := time.NewTicker(time.Second)
    defer ticker.Stop() // 確保停止ticker

    i := 1
    for {
        select {
        case <-ticker.C: // 定時(shí)觸發(fā)
            fmt.Println(i)
            i++
            if i > 10 {
                return // 自然結(jié)束
            }

        case <-ctx.Done(): // 上下文取消
            fmt.Println("任務(wù)取消:", ctx.Err())
            return
        }
    }
}

context 包深度解析

核心優(yōu)勢(shì)

  • 取消傳播:一次取消,所有監(jiān)聽組件都能收到通知
  • 超時(shí)控制:可添加 WithTimeout 自動(dòng)取消
  • 資源安全:defer 確保資源釋放
  • 標(biāo)準(zhǔn)統(tǒng)一:Go 標(biāo)準(zhǔn)庫廣泛使用

實(shí)際應(yīng)用

// 帶超時(shí)的context
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

// 在數(shù)據(jù)庫查詢中使用
err := db.QueryContext(ctx, "SELECT...")

適用場(chǎng)景:所有生產(chǎn)環(huán)境需要生命周期管理的并發(fā)任務(wù)

6. 方法對(duì)比

方案順序間隔可取消資源回收復(fù)雜度適用場(chǎng)景
Sleep?一般???簡(jiǎn)單腳本
After?????單次超時(shí)
Ticker??????周期性任務(wù)
Channel 并發(fā)???????并行獨(dú)立任務(wù)(不要求順序)
WaitGroup 并發(fā)??????并行獨(dú)立任務(wù)(簡(jiǎn)單同步)
單 goroutine+WG??????順序定時(shí)任務(wù)
select+Ticker????*????多事件源監(jiān)控
context+Ticker????????生產(chǎn)級(jí)定時(shí)任務(wù)(推薦)

*需自行實(shí)現(xiàn)取消通道

7. 完整可運(yùn)行示例

package main

import (
    "context"
    "fmt"
    "sync"
    "time"
)

// 所有實(shí)現(xiàn)函數(shù)(前面已詳細(xì)解釋)

func main() {
    fmt.Println("=== 基礎(chǔ): time.Sleep ===")
    UseTimeSleep()

    fmt.Println("\n=== 基礎(chǔ): time.After ===")
    UseTimeAfter()

    fmt.Println("\n=== 基礎(chǔ): time.Ticker ===")
    UseTimeTicker()

    fmt.Println("\n=== 誤區(qū): Channel并發(fā) ===")
    UseChannel()

    fmt.Println("\n=== 誤區(qū): WaitGroup并發(fā) ===")
    UseGoroutine()

    fmt.Println("\n=== 正確: 單goroutine+WaitGroup ===")
    UseSingleGoroutine()

    fmt.Println("\n=== 正確: select+Ticker ===")
    UseSelectAndTicker()

    fmt.Println("\n=== 生產(chǎn)推薦: context+Ticker ===")
    UseContextWithTicker()

    fmt.Println("\n=== 所有示例執(zhí)行完成 ===")
}

運(yùn)行方式

# 運(yùn)行程序
go run main.go

# 輸出示例:
=== 基礎(chǔ): time.Sleep ===
1
2
...
10

=== 誤區(qū): Channel并發(fā) ===
1
3
2
... # 順序隨機(jī)

進(jìn)階學(xué)習(xí)建議

context 的更多用法

  • context.WithTimeout:自動(dòng)超時(shí)取消
  • context.WithValue:傳遞請(qǐng)求范圍數(shù)據(jù)
  • 上下文傳遞規(guī)范

錯(cuò)誤處理模式

select {
case <-ctx.Done():
    return ctx.Err()
case err := <-errCh:
    return err
}

資源管理最佳實(shí)踐

  • 總是 defer 關(guān)閉資源
  • 使用sync.Once確保單次初始化
  • 避免在循環(huán)中創(chuàng)建 goroutine

性能調(diào)優(yōu)

  • 使用pprof分析 goroutine 泄漏
  • 合理設(shè)置 GOMAXPROCS
  • 避免過度并發(fā)

實(shí)際項(xiàng)目中的并發(fā)往往更復(fù)雜,但核心原理不變。掌握這些基礎(chǔ)模式,你就能構(gòu)建健壯的并發(fā)系統(tǒng)!

到此這篇關(guān)于Go語言并發(fā)定時(shí)任務(wù)之從Sleep到Context的8種寫法全解析的文章就介紹到這了,更多相關(guān)Go語言并發(fā)定時(shí)任務(wù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go-Web框架中AOP方案的實(shí)現(xiàn)方式

    Go-Web框架中AOP方案的實(shí)現(xiàn)方式

    本文主要介紹了Go-Web框架中AOP方案的實(shí)現(xiàn)方式,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • 使用Go語言解決Scan空格結(jié)束輸入問題

    使用Go語言解決Scan空格結(jié)束輸入問題

    這篇文章主要為大家介紹了使用Go語言來解決Scan空格結(jié)束輸入問題,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2021-11-11
  • Golang配置管理Viper的實(shí)現(xiàn)

    Golang配置管理Viper的實(shí)現(xiàn)

    Viper是Go語言配置管理庫,支持JSON/YAML/TOML等多格式,可動(dòng)態(tài)監(jiān)聽配置變更,本文就來介紹一下Golang配置管理Viper的實(shí)現(xiàn),感興趣的可以了解一下
    2025-07-07
  • 使用go語言實(shí)現(xiàn)Redis持久化的示例代碼

    使用go語言實(shí)現(xiàn)Redis持久化的示例代碼

    redis 是一個(gè)內(nèi)存數(shù)據(jù)庫,如果你把進(jìn)程殺掉,那么里面存儲(chǔ)的數(shù)據(jù)都會(huì)消失,那么這篇文章就是來解決 redis 持久化的問題,本文給大家介紹了使用go語言實(shí)現(xiàn)Redis持久化,需要的朋友可以參考下
    2024-07-07
  • Golang基于Vault實(shí)現(xiàn)敏感數(shù)據(jù)加解密

    Golang基于Vault實(shí)現(xiàn)敏感數(shù)據(jù)加解密

    數(shù)據(jù)加密是主要的數(shù)據(jù)安全防護(hù)技術(shù)之一,敏感數(shù)據(jù)應(yīng)該加密存儲(chǔ)在數(shù)據(jù)庫中,降低泄露風(fēng)險(xiǎn),本文將介紹一下利用Vault實(shí)現(xiàn)敏感數(shù)據(jù)加解密的方法,需要的可以參考一下
    2023-07-07
  • go中值傳遞和指針傳遞的使用

    go中值傳遞和指針傳遞的使用

    在Go語言中,使用&和*可以分別取得變量的地址和值,解引用未初始化或?yàn)閚il的指針會(huì)引發(fā)空指針異常,正確的做法是先進(jìn)行nil檢查,此外,nil在Go中用于多種類型的空值表示,值傳遞和指針傳遞各有適用場(chǎng)景,通常小型數(shù)據(jù)結(jié)構(gòu)優(yōu)先考慮值傳遞以減少解引用開銷
    2024-10-10
  • go語言中函數(shù)與方法介紹

    go語言中函數(shù)與方法介紹

    這篇文章介紹了go語言中的函數(shù)與方法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-07-07
  • gorm FirstOrCreate和受影響的行數(shù)實(shí)例

    gorm FirstOrCreate和受影響的行數(shù)實(shí)例

    這篇文章主要介紹了gorm FirstOrCreate和受影響的行數(shù)實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • 詳解Go語言中的結(jié)構(gòu)體的特性

    詳解Go語言中的結(jié)構(gòu)體的特性

    結(jié)構(gòu)體是Go語言中重要且靈活的概念之一,本文旨在深入介紹Go語言中的結(jié)構(gòu)體,揭示其重要性和靈活性,并向讀者展示結(jié)構(gòu)體支持的眾多特性,需要的可以參考一下
    2023-06-06
  • Go源碼字符串規(guī)范檢查lint工具strchecker使用詳解

    Go源碼字符串規(guī)范檢查lint工具strchecker使用詳解

    這篇文章主要為大家介紹了Go源碼字符串規(guī)范檢查lint工具strchecker使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06

最新評(píng)論