【发布时间】: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 上描述了该行为:“仅当缓冲区已满时发送到缓冲通道块。当缓冲区为空时接收块。”