7

我正在学习 Go,并且正在学习 GoTours 的这一课。这是我到目前为止所拥有的。

package main

import (
    "fmt"
    "code.google.com/p/go-tour/tree"
)

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

func main() {
    var ch chan int = make(chan int)
    go Walk(tree.New(1), ch)
    for c := range ch {
        fmt.Printf("%d ", c)    
    }
}

如您所见,我尝试通过将写入通道的值打印出来来测试我的 Walk 功能。但是,我收到以下错误。

1 2 3 4 5 6 7 8 9 10 throw: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
    main.go:25 +0x85

goroutine 2 [syscall]:
created by runtime.main
    /usr/local/go/src/pkg/runtime/proc.c:221

exit status 2

我认为这个错误应该是预料之中的,因为我从来没有close这个频道。但是,有没有办法我可以“捕捉”这个死锁错误并以编程方式处理它?

4

5 回答 5

8

这会导致死锁,因为range构造会迭代直到通道关闭。 http://golang.org/ref/spec#For_statements

在这里,您需要在完全探索树时关闭通道或使用其他构造。

对于此示例,您知道树的大小为 10,因此您可以简单地执行从 1 到 10 的 for 循环,并在每次迭代时从通道读取一次。

于 2012-12-29T04:39:47.917 回答
8

死锁类似于 nil 指针引用,它代表程序中的 BUG。由于这个原因,这类错误通常是不可恢复的。

正如 lbonn 提到的,这里的问题是您需要“关闭(myChan)”您的频道。如果你不这样做 for-range 循环,那么循环将永远等待下一个元素。

你可以尝试这样的事情:

func main() {
    var ch chan int = make(chan int)
    go func() {
        Walk(tree.New(1), ch)
        close(ch)
    }()
    for c := range ch {
        fmt.Printf("%d ", c)
    }
}

如果要并行遍历树,则需要进行进一步的更改:

package main

import (
    "code.google.com/p/go-tour/tree"
    "fmt"
    "sync"
)

// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int, done *sync.WaitGroup) {
    if t != nil {
        done.Add(2)
        go Walk(t.Left, ch, done) //look at each branch in parallel
        go Walk(t.Right, ch, done)
        ch <- t.Value
    }
    done.Done()
}

func main() {
    var ch chan int = make(chan int, 64) //note the buffer size
    go func() {
        done := new(sync.WaitGroup)
        done.Add(1)
        Walk(tree.New(1), ch, done)
        done.Wait()
        close(ch)
    }()
    for c := range ch {
        fmt.Printf("%d ", c)
    }
}
于 2012-12-30T00:04:13.160 回答
6

不,您无法从死锁中恢复。

于 2012-12-28T21:08:42.330 回答
0

通道死锁错误为:

致命错误:所有 goroutine 都处于休眠状态 - 死锁!

通道死锁不是panic错误,是致命错误,请参阅https://golang.org/pkg/log/#Fatal

Fatal 等价于 Print(),然后调用 os.Exit(1)。

正如你所看到的,最后Fatal会调用os.Exit(1),所以它与 完全不同panic,这就是为什么它不能recover

于 2019-10-14T14:31:25.837 回答
0

我想出了这个解决方案,它基本上使用 2 个通道,如果两个通道都关闭,则得出的结论是树是相同的。

package main

import "golang.org/x/tour/tree"
import "fmt"

// 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 {
        return
    }
    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 {
    tc1 := make(chan int)
    tc2 := make(chan int)

    go func() {
        Walk(t1, tc1)
        close(tc1)
    }()
    go func() { 
       Walk(t2, tc2)
       close(tc2)
    }()

    for {
        x1, y1 := <-tc1;
        x2, y2 := <-tc2;
        if  x1 != x2  {
            return false
        }
        if !y1 || !y2 {
            return true
        }
    }
}


func main() {
    t1, t2 := tree.New(123), tree.New(1)
    fmt.Println(Same(t1, t2))
}

我正在关注 go-tour guide 并且只使用了迄今为止教授的资源(与在上述解决方案中使用同步包不同。)

于 2020-09-26T15:56:16.303 回答