在永久循环中读取两个通道的输出

时间:2018-05-04 14:53:59

标签: go

我正在进行tour.golang的树木练习。我试图实现与下面写的相同的功能。

func Same(t1, t2 *tree.Tree) bool {
    ch1 := make(chan int)
    ch2 := make(chan int)
    go Walk(t1, ch1);
    go Walk(t2, ch2);

    for c := range ch1 {
        d := <- ch2
        if c-d !=0 {
            return false
        }   

    }
    return true
}

使用forever循环,我想比较ch1的输出是否与ch2的输出不同。但是以下是抛出这个错误:

  

致命错误:所有goroutine都睡着了 - 死锁!

live version

3 个答案:

答案 0 :(得分:3)

你应该在遍历树之后关闭通道以终止范围循环,以防树木相等(要注意:Same当树的中缀遍历相等时返回true,它们的结构不是要求是平等的。)

func WalkTreeAndThenCloseChannel(t *tree.Tree, ch chan int) {
    Walk(t, ch)
    close(ch)
}

func Same(t1, t2 *tree.Tree) bool {
    ch1 := make(chan int)
    ch2 := make(chan int)
    go WalkTreeAndThenCloseChannel(t1, ch1);
    go WalkTreeAndThenCloseChannel(t2, ch2);

注意:您应该检查第二个频道是否已经关闭,以防树木的项目数量不同而且找不到差异(&#34;线程饥饿&#34;这里的术语比&更合适#34;死锁&#34;。)

答案 1 :(得分:3)

您看到一个死锁的原因非常简单:您的范围超过ch1,但从未关闭它,因此for循环永远不会终止。

您可以通过在main()中手动迭代每个树只有一些次数来修复此问题,例如0..10循环:

// 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 := 0; i < 10; i++ {
        c := <-ch1
        d := <-ch2
        if c-d != 0 {
            return false
        }

    }
    return true
}

Playground

或者,您可以更改Walk的签名以接受由Walk的调用者递增的waitgroup参数,并在每次Walk返回时递减,并且在执行步行后返回goroutine以关闭通道:

// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int, wg *sync.WaitGroup) {
    defer wg.Done()
    if t.Left != nil {
        wg.Add(1)
        Walk(t.Left, ch, wg)
    }
    ch <- t.Value
    if t.Right != nil {
        wg.Add(1)
        Walk(t.Right, ch, wg)
    }

}

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

    var wg1 sync.WaitGroup
    wg1.Add(1)
    go Walk(t1, ch1, &wg1)
    go func() {
        wg1.Wait()
        close(ch1)
    }()

    var wg2 sync.WaitGroup
    wg2.Add(1)
    go Walk(t2, ch2, &wg2)
    go func() {
        // not strictly necessary, since we're not ranging over ch2, but here for completeness
        wg2.Wait()
        close(ch2)
    }()

    for c := range ch1 {
        d := <-ch2
        if c-d != 0 {
            return false
        }

    }
    return true
}

Playground

答案 2 :(得分:-1)

这里有一个问题,你没有在walk函数中为右子树的通道发送值。但是在另一边接收它就是为什么死锁错误。因为在从未发送的右子树的情况下,您从通道接收值。