我正在尝试做这个教程 -
https://tour.golang.org/concurrency/8
这是我的代码
// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
if t != nil {
Walk(t.Left, ch)
ch <- t.Value
Walk(t.Right,ch)
}
}
// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
ch1 := make(chan int)
ch2 := make(chan int)
go Walk(t1, ch1)
go Walk(t2, ch2)
for i:= range ch1 {
if i != <-ch2 {
return false
}
}
return true
}
func main() {
isSame := Same(tree.New(1), tree.New(1))
if isSame {
fmt.Println("SAME")
} else {
fmt.Println("DIFF")
}
}
但是我收到了这个错误 -
fatal error: all goroutines are asleep - deadlock!
它运作了一次,然后我再次运行它并且它停止工作......或者我或者我疯了。
发生了什么?
答案 0 :(得分:3)
问题是你永远不会关闭ch1
,所以你的for i:= range ch1
循环永远不会结束;它只是读取,直到通道中没有值,然后它阻塞。此时,将只有一个goroutine,并且它被阻止听空信道,所以Go将中止您看到的消息。 (同样地,你永远不会关闭ch2
,但在你的情况下,这不会发生。如果ch2
的值低于ch1
,那么就会导致死锁。)
说实话,我不确定究竟是什么解决方案“Tour of Go”的人心目中。
一个可行但完全作弊的选项是硬编码你只会看到十个值的事实:
for i := 0; i < 10; i++ {
if <-ch1 != <-ch2 {
return false
}
}
更好的选择是Same
使用匿名函数调用Walk
然后关闭频道:
go func() {
Walk(t1, ch1)
close(ch1)
}()
(或者您可以使用非匿名函数;将其称为walkAndClose
或其他内容。)
顺便提一下,您的Same
函数假定这两棵树的大小相同。如果t1
包含更多元素,那么t2
将在末尾隐式填充零(因为<-ch2
在0
关闭并耗尽时返回ch2
,如果t1
更短,那么t2
将在结尾隐式截断。也许你是O.K.为了本练习的目的而做出这个假设,但是如果你希望Same
总是为不同大小的树返回false
,你可以将你的循环改为:
for i := range ch1 {
j, receivedJ := <-ch2
if i != j || ! receivedJ {
return false
}
}
_, receivedJ := <-ch2
if receivedJ {
return false
}
(我使用<-ch2
的双返回值形式来检测频道是否已关闭且已用尽;如果receivedJ
true
将为<-ch2
}从频道返回实际值,如果默认返回false
,则返回0
。