2012-12-28 58 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循環,並在每次迭代中從通道讀取一次。