如何捕获通道死锁的异常?

dan*_*007 7 go

我正在学习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)    
    }
}
Run Code Online (Sandbox Code Playgroud)

如您所见,我尝试通过打印出我写入频道的值来测试我的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
Run Code Online (Sandbox Code Playgroud)

我认为应该预料到这个错误,因为我从不close通道.但是,有没有办法可以"捕获"这个死锁错误并以编程方式处理它?

lbo*_*onn 7

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

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

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


voi*_*gic 6

死锁类似于nil指针deference,因为它代表程序中的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)
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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)
    }
}
Run Code Online (Sandbox Code Playgroud)


zzz*_*zzz 5

不,你无法从僵局中恢复过来.