我正在尝试完成本教程-
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!
它曾经工作过一次,然后我再次运行它又停止工作了……要么那样,要么我疯了。
这是怎么回事?
最佳答案
问题是您永远不会关闭ch1
,因此您的for i:= range ch1
循环永远不会结束;它只会读取直到 channel 中没有值,然后才阻塞。此时,只有一个goroutine,并且已阻止监听空 channel ,因此Go会中止您看到的消息。 (类似地,您从不会关闭ch2
,但是在您的情况下这并不重要。如果ch2
的值小于ch1
,那将导致死锁。)
老实说,我不确定“Tour of Go”的人们想出了什么解决方案。
一个可行但完全作弊的选择是硬编码一个事实,即您只会看到十个值:
for i := 0; i < 10; i++ {
if <-ch1 != <-ch2 {
return false
}
}
更好的选择是让
Same
使用匿名函数来调用Walk
,然后关闭 channel :go func() {
Walk(t1, ch1)
close(ch1)
}()
(或者您可以为此使用非匿名函数;将其称为
walkAndClose
或其他名称)。顺便说一句,您的
Same
函数假定两个树的大小相同。如果t1
具有更多元素,则t2
将在末尾隐式填充零(因为一旦<-ch2
关闭并用尽,由于0
返回ch2
),并且如果t1
较短,则t2
将在末尾隐式截断。也许你还好在本练习中进行此假设,但是如果您希望Same
始终为不同大小的树返回false
,则可以将循环更改为:for i := range ch1 {
j, receivedJ := <-ch2
if i != j || ! receivedJ {
return false
}
}
_, receivedJ := <-ch2
if receivedJ {
return false
}
(我使用
<-ch2
的双返回值形式来检测 channel 是否已关闭和耗尽;如果receivedJ
从 channel 返回实际值,则true
将为<-ch2
;如果默认情况下返回false
,则0
将为ojit_code) 。