我正在尝试完成本教程-

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) 。

10-08 09:19