簡體   English   中英

使用范圍通道中斷 for 語句時會發生什么

[英]What happens when you break the for statement with a range channel

我正在按照此代碼獲取帶有頻道的惰性數字范圍

// iterator
func iterator(n int, c chan int) {
    for i := 0; i < n; i++ {
        c <- i
    }
    close(c)
    fmt.Println("iterator End")
}

c := make(chan int)
go iterator(5, c)
for i := range c {
    fmt.Println(i)
}

這將按預期打印

0
1
2
3
4
fmt.Println("iterator End")

但是當我像這樣打破for循環時發生了什么

c := make(chan int)
go getNumbers(5, c)
for i := range c {
    if i == 2 {
        break
    }
    fmt.Println(i)
}

似乎 goroutine 被阻塞了,因為從不打印iterator End (我也嘗試通過休眠主線程)。 我想知道如何處理這種情況? 我需要使用select來解決這個問題嗎? 有什么安全的方法可以檢查范圍是否中斷並停止迭代器中的for 循環

如果一個 goroutine 寫入一個無緩沖的通道並且沒有其他 goroutine 從該通道讀取 - 那么寫入將永遠阻塞。 這將導致 goroutine 泄漏。 這就是你正在經歷的。

如果你有一個寫入通道的“生產者”goroutine,你需要一種方法來通知它停止。 關閉通道不是這里的關鍵部分 - 因為通道在 scope 中的 go 時被垃圾收集。阻塞的 goroutine(永遠不會解除阻塞)被認為是泄漏,因為它永遠不會被回收,所以你真的需要 goroutine 結束.

您可以通過多種方式表示退出的意圖 - 最流行的兩種方式是:

信號:完成通道

func iterator(n int, c chan int, done <-chan struct{}) {
    for i := 0; i < n; i++ {
        select {
        case c <- i:
        case <-done:
            break
        }
    }
    close(c)
    fmt.Println("iterator End")
}

讀者協程:

c := make(chan int)
done := make(chan struct{})
go iterator(5, c, done)
for i := range c {
    if i == 2 {
        break
    }
    fmt.Println(i)
}
close(done) // signal writer goroutine to quit

信號:context.Context

func iterator(ctx context.Context, n int, c chan int) {
        defer close(c)
        defer fmt.Println("iterator End")

        for i := 0; i < n; i++ {
                select {
                case c <- i:
                case <-ctx.Done():
                        fmt.Println("canceled. Reason:", ctx.Err())
                        return
                }
        }
}

讀取協程:

func run(ctx context.Context) {
        ctx, cancel := context.WithCancel(ctx)
        defer cancel()  // call this regardless - avoid context leaks - but signals producer your intent to stop
        c := make(chan int)
        go iterator(ctx, 5, c)
        for i := range c {
                if i == 2 {
                        break
                }
                fmt.Println(i)
        }
}

https://play.golang.org/p/4-fDyCurB7t

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM