首頁  >  文章  >  後端開發  >  我們如何保證取消的上下文會導致 goroutine 終止?

我們如何保證取消的上下文會導致 goroutine 終止?

PHPz
PHPz轉載
2024-02-08 23:54:23974瀏覽

我们如何保证取消的上下文会导致 goroutine 终止?

php小編子墨將為大家介紹如何保證取消上下文會導致goroutine終止的方法。當我們在使用goroutine時,有時需要在某個條件滿足時取消它,以避免不必要的計算和資源浪費。為了確保goroutine能夠正確終止,我們可以使用context套件提供的機制。 context包提供了一種在goroutine之間傳遞請求的方法,並且可以在需要時取消這些請求。透過合理地使用context包,我們可以確保goroutine在取消上下文時能夠正確地終止,從而避免資源洩漏和其他潛在的問題。下面我們將詳細介紹如何使用context套件來實現這一目標。

問題內容

假設發生下列情況:

  • 我們有下面的 consumer 函數,在 goroutine 中運行。

  • 另一個 goroutine 正在 intchan 通道上毫無延遲地發送整數。換句話說,在 for 迴圈的每次迭代中,intchan 上都有一個準備好接收的值。

  • 啟動 consumer goroutine 的 goroutine 已取消傳遞到 consumer 的上下文。因此,ctx.done() 通道也有一個可供接收的值。

問題:

  • 在這種情況下,select 語句的兩種情況都已準備好運行。
  • 根據 go 之旅,select 將隨機選擇一種情況,因為兩者都已準備好運行。
  • 如何保證 select 不會繼續選擇 <- intchan 案例?如果<- cx.done() 案例在for 迴圈的每次迭代中都準備就緒,我們如何知道<- ctsx.done() 案例最終會被選中?
func consumer(ctx context.context, intchan chan int) {
    for {
        select {
        case <-ctx.done():
            return
        case i := <-intchan:
            foo(i)
        }
    }
}

我嘗試在下面的程式中使用 consumer 函數。 在該程式的多次運行中,consumerproducer goroutine 似乎總是終止。

為什麼我們最終不會以 <-ctx.done() 案例從未執行的運行結束?

package main

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

func main() {

    ctx, cancelFunc := context.WithCancel(context.Background())

    var wg sync.WaitGroup
    wg.Add(2) // add 2, because we spawn 2 goroutines

    Producer(ctx, &wg)

    fmt.Println(time.Now())
    time.Sleep(time.Second * 5) // cancel the context after 5 seconds

    cancelFunc()
    fmt.Println("CANCELLED")

    wg.Wait() // wait till both producer and consumer goroutines terminate
    fmt.Println(time.Now())

}

func Producer(ctx context.Context, wg *sync.WaitGroup) {
    intChan := make(chan int)

    go Consumer(ctx, intChan, wg)

    go func() {
        defer wg.Done()
        for {
            select {
            case <-ctx.Done():
                return
            case intChan <- 1:
            }
        }
    }()

}

func Consumer(ctx context.Context, intChan chan int, wg *sync.WaitGroup) {
    defer wg.Done()

    for {
        select {
        case <-ctx.Done():
            return
        case _ = <-intChan:
        }
    }
}

解決方法

沒有任何保證。保證終止的最簡單方法是在 select 語句之外使用 ctx.err() 檢查錯誤。將錯誤返回傳遞上下文的程式碼也很常見。我會這樣寫 consumer 函數:

func Consumer(ctx context.Context, intChan chan int) error {
    for ctx.Err() == nil {
        select {
        case <-ctx.Done():
        case i := <-intChan:
            foo(i)
        }
    }
    return ctx.Err()
}

以上是我們如何保證取消的上下文會導致 goroutine 終止?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:stackoverflow.com。如有侵權,請聯絡admin@php.cn刪除