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

时间:2012-12-28 20:29:49

标签: go

我正在学习Go并正在GoTours工作this lesson。这是我到目前为止所拥有的。

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频道。但是,有没有办法可以“捕获”这个死锁错误并以编程方式处理它?

5 个答案:

答案 0 :(得分:7)

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

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

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

答案 1 :(得分: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)
    }
}

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

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

答案 2 :(得分:5)

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

答案 3 :(得分:0)

通道死锁错误是:

  

致命错误:所有goroutine都在睡觉-死锁!

通道死锁不是{"schedule":"0 */5 * * * *"}错误,而是致命错误,请参见https://golang.org/pkg/log/#Fatal

  

致命等同于Print(),然后调用os.Exit(1)。

如您所见,public static void MyFunction([TimerTrigger("0 */2 * * * *", RunOnStartup = true)] TimerInfo timerInfo, TextWriter log) 最后会调用panic,因此与Fatal完全不同,这就是为什么不能os.Exit(1)

答案 4 :(得分: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))
}

我正在遵循导游指南,到目前为止只使用了所讲授的资源(与上述解决方案中使用同步包不同)。