我有一个接受一个int数组并将其转储到通道中的函数。
func Dump(a []int, ch chan int) {
for i := range a {
ch <- i
}
close(ch)
}
此主要版本未构建:
func main() {
ch := make(chan int)
arr := []int{1, 2, 3, 4, 5}
Dump(arr, ch)
for i := range ch {
fmt.Printf("Got %v\n", i)
}
}
引发此错误:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.Dump(0xc000078f48, 0x5, 0x5, 0xc00006e060)
/Users/300041738/go-workspace/src/test.go:7 +0x43
main.main()
/Users/300041738/go-workspace/src/test.go:15 +0x9b
exit status 2
但是,这会建立:
func main() {
ch := make(chan int)
arr := []int{1, 2, 3, 4, 5}
go Dump(arr, ch)
for i := range ch {
fmt.Printf("Got %v\n", i)
}
}
为什么我必须在转储前写go?我不希望异步转储数组的内容。
答案 0 :(得分:3)
通道具有缓冲区。默认情况下,缓冲区的大小为0。换句话说,如果要将元素插入到非缓冲通道中,则插入的goroutine将暂停,直到另一个goroutine从通道中检索值为止。
所以很有趣,试试这个:
import "fmt"
func Dump(a []int, ch chan int) {
for i := range a {
ch <- i
}
close(ch)
}
func main() {
arr := []int{1, 2, 3, 4, 5}
ch := make(chan int, len(arr)) //specify channel buffer length equal to arr size
Dump(arr, ch)
for {
i, ok := <- ch
if ok {
fmt.Println("received a number !", i)
} else {
fmt.Println("channel is closed, we're done here")
}
}
}
答案 1 :(得分:2)
引起Dealock的原因是您的主要goroutine试图写入通道,但没有人从该通道读取数据。您无需在此处使用缓冲通道。
有一个流水线概念。您的Dump
函数基本上就像这里的管道源一样。您可以将Dump
函数修改为如下形式:
func Dump(a []int) chan int {
ch := make(chan int)
go func() {
for i := range a {
ch <- i
}
close(ch)
}()
return ch
}
请注意,我现在正在单独的go例程中写入通道。
答案 2 :(得分:1)
问题的根源-当您写入无缓冲通道时,该通道将被锁定,直到有人从该通道读取值为止。 对于无缓冲通道-在每次写操作之后,您必须执行一次读操作。否则,您的第二次写操作将被锁定,直到有人读取了第一个值。在您的第一个示例中,它永远不会发生。
说明:
Dump()
调用for
循环的第一次迭代i
值写入通道for
循环的第二次迭代i
值写入通道,但锁定,没有人读取第一个值。在添加goroutine用法(go Dump(arr, ch)
)时会发生这种情况:
Dump()
函数for
循环的第一次迭代i
值写入通道for
循环的第二次迭代i
值写入通道,但锁定,没有人读取第一个值。Dump()
的地方),但是我们拥有主goroutine,而go调度程序将控件切换到了主goroutine。 for i := range ch
。并最终读取第一个值!Dump()
goroutine已解锁,可以写入第二个值了。请注意,执行的确切顺序可能有所不同(基于go调度程序的逻辑)。要使用它,您可以使用Dump()
将打印添加到go Dump(arr, ch)
函数中:
func Dump(a []int, ch chan int) {
for i := range a {
fmt.Printf("Write %v\n", i)
ch <- i
}
close(ch)
}
您将看到Write和Got消息会混合在一起。
很难为您的答案提供解决方案,因为它是一个沙盒示例,实际上并不需要该通道。
通过使用缓冲通道,假设大小为n
,您可以进行n
写操作而不会锁定和读取。
我建议您阅读basics of Go channels。