英文:
How do I catch the exception of a channel deadlocking?
问题
我正在学习Go,并且正在从GoTours的这个课程中学习。这是我目前的代码。
package main
import (
"fmt"
"code.google.com/p/go-tour/tree"
)
// Walk遍历树t,将树中的所有值发送到通道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
我认为这个错误是可以预料的,因为我从未“关闭”通道。然而,有没有办法我可以以编程方式“捕捉”这个死锁错误并处理它呢?
英文:
I am learning Go and am working on this lesson from the GoTours. Here's what I have so far.
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)
}
}
As you see, I try to test out my Walk function by printing out the values I wrote into a channel. However, I get the following error.
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
This error should be expected I think because I never close
the channel. However, is there a way I could "catch" this deadlock error and deal with it programmatically?
答案1
得分: 8
这个死锁是因为range
构造会迭代直到通道关闭。
在这里,你需要在树完全被探索完毕时关闭通道,或者使用另一种构造方式。
对于这个例子,你知道树的大小是10,所以你可以简单地使用一个循环从1到10,并在每次迭代时从通道中读取一次。
英文:
This deadlocks because, the range
construct iterates until the channel is closed.
http://golang.org/ref/spec#For_statements
Here, you need to either close the channel when the tree is fully explored or use another construct.
For this example, you know that the trees are of size 10, so you can simply do a for loop from 1 to 10 and read from the channel once at each iteration.
答案2
得分: 8
死锁类似于空指针解引用,因为它表示程序中的一个错误。由于这个原因,这类错误通常是不可恢复的。
正如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遍历树t,将树中的所有值发送到通道ch。
func Walk(t *tree.Tree, ch chan int, done *sync.WaitGroup) {
if t != nil {
done.Add(2)
go Walk(t.Left, ch, done) //并行查看每个分支
go Walk(t.Right, ch, done)
ch <- t.Value
}
done.Done()
}
func main() {
var ch chan int = make(chan int, 64) //注意缓冲区大小
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)
}
}
英文:
Deadlock is similar to a nil pointer deference in that is represents a BUG in your program. This class of error is usually not recoverable for this reason.
As lbonn mentioned, the problem here is you need to "close(myChan)" your channel. If you do not do this the for-range loop, that the loop will wait for the next element forever.
You can try something like this:
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)
}
}
If you want to traverse the tree in parallel you will need to make further changes:
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)
}
}
答案3
得分: 6
不,你无法从死锁中恢复。
英文:
No, you cannot recover from a deadlock.
答案4
得分: 0
通道死锁错误是:
致命错误:所有的goroutine都处于休眠状态 - 死锁!
通道死锁不是一个panic
错误,它是一个致命错误,参见https://golang.org/pkg/log/#Fatal
Fatal等同于Print()后跟os.Exit(1)的调用。
正如你所看到的,Fatal
最后会调用os.Exit(1)
,所以它与panic
完全不同,这就是为什么它不能被recover
的原因。
英文:
The channel deadlock error is:
> fatal error: all goroutines are asleep - deadlock!
channel deadlock is not a panic
error, it's a fatal error, see https://golang.org/pkg/log/#Fatal
> Fatal is equivalent to Print() followed by a call to os.Exit(1).
As you can see, Fatal
will call os.Exit(1)
at last, so it's totally different with panic
, and that's why it can't be recover
答案5
得分: 0
我提出了这个解决方案,它基本上使用了2个通道,并且当两个通道都关闭时,得出树是相同的结论。
package main
import "golang.org/x/tour/tree"
import "fmt"
// Walk函数遍历树t,并将所有值发送到通道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函数确定树t1和t2是否包含相同的值。
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指南进行操作,并且只使用了到目前为止所学的资源(与上述解决方案中使用的sync包不同)。
英文:
I came up with this solution, It basically uses 2 channels and concludes the trees are same if both the channels are closed.
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))
}
I'm following go-tour guide and only used resources taught so far (Unlike using sync package in the above solution.)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论