2012-12-28 55 views
7

我正在学习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通道。但是,有没有一种方法可以“捕捉”这个死锁错误并以编程方式处理它?

+2

根据定义,僵局意味着所有够程没有运行。如果什么都没有运行,就没有可以“捕捉”异常的配置程序。 – newacct

+0

谢谢!如果是这样的话,在我读完n次后,有没有一种方法可以停止阅读频道,其中'n'是我写入频道的次数? – dangerChihuahua007

+2

另一个提示:在Walk功能中指定通道方向是一种很好的做法,例如 func Walk(t * tree.Tree,ch chan < - int) –

回答

6

死锁类似于无指针顺从,因为它表示程序中的BUG。由于这个原因,这类错误通常无法恢复。

正如lbonn所说,这里的问题是你需要“关闭(myChan)”你的频道。如果你不这样做的范围循环,该循环将永远等待下一个元素。

你可以尝试这样的事情:

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

不,你不能从死锁中恢复过来。

+1

谢谢!但是,如何将我发送到频道的内容打印出来? – dangerChihuahua007

7

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

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

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