【问题标题】:Deadlock in the program when using for range on buffered channel在缓冲通道上使用范围时程序中的死锁
【发布时间】:2019-09-15 02:15:24
【问题描述】:

我正在学习 Go 并且正在使用 Goroutine 和通道。我正在编写一个非常做作和天真的工作池,使用两个缓冲通道,一个用于输入,一个用于输出。

现在我在向其添加作业后关闭输入通道,然后最终读取输出通道以从中读取结果,但是当我使用for val := range ch 输出程序时出现死锁。这是示例代码

package main

import (
    "fmt"
    "time"
)

func main() {
    st := time.Now()

    jobs := make(chan int, 100)
    res := make(chan int, 100)

    // Putting items to the jobs channel
    for i := 0; i < 9; i++ {
        jobs <- i
    }
    close(jobs)

    go workerPool(jobs, res, 1)


    // This causes the program to panic with deadlock
    for v := range res {
        fmt.Println(v)
    }

// This works just fine and program does not panics
    //for i := 0; i < 9; i++ {
    //  fmt.Println(<-res)
    //}

    ed := time.Now()
    fmt.Println(ed.Sub(st))
}

func workerPool(ip <-chan int, op chan<- int, id int) {
    for v := range ip {
        fmt.Println("Worker", id, "working on ", v)
        op <- 1
    }
}

这是我看到的输出

Worker 1 working on  0
Worker 1 working on  1
Worker 1 working on  2
Worker 1 working on  3
Worker 1 working on  4
Worker 1 working on  5
Worker 1 working on  6
Worker 1 working on  7
Worker 1 working on  8
1
1
1
1
1
1
1
1
1
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()

我知道,当我使用传统的 for loop 时,我正在获取我最初放入 jobs 频道的确切数量的值 (9)。但是for range 不应该自动处理这个问题,当没有更多数据要从通道读取时,通道将发送false 值并且循环将终止?

【问题讨论】:

  • range 运算符只会在通道关闭后终止。 gobyexample.com/range-over-channels 您可能会将此与 select 的行为混淆,后者实际上会评估 cases 从封闭通道读取数据。
  • "当没有更多数据要从通道读取时"它会一直等到有;如果没有,无缓冲通道将根本无法工作。 Per the Tour of Go, "发送和接收块,直到对方准备好"。
  • @Adrian 你可以看到这是一个缓冲通道,我知道无缓冲通道,但问题是关于缓冲通道。
  • 没关系。如果您尝试从一个空的缓冲通道接收,它仍然会等待直到有数据。如果您尝试发送到一个完整的缓冲通道,它仍然会等到有消费者。缓冲通道与无缓冲通道没有显着差异。行为完全相同,但有一个缓冲区。
  • the very next page of the Tour 上描述了该行为:“仅当缓冲区已满时发送到缓冲通道块。当缓冲区为空时接收块。”

标签: go channel goroutine


【解决方案1】:

这个问题的解决方案非常明显,我所要做的就是close worker 中的输出通道,range 开始正常工作,原因很明显。

func workerPool(ip <-chan int, op chan<- int, id int) {
    for v := range ip {
        fmt.Println("Worker", id, "working on ", v)
        op <- fib(v)
    }
    close(op) // change made 
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-08-15
    • 1970-01-01
    • 2021-12-25
    • 2017-09-06
    • 2017-12-14
    • 2014-05-26
    • 2019-05-14
    • 1970-01-01
    相关资源
    最近更新 更多