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

huangapple go评论93阅读模式
英文:

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 (
	&quot;fmt&quot;
	&quot;code.google.com/p/go-tour/tree&quot;
)

// 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 &lt;- 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(&quot;%d &quot;, 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(&quot;%d &quot;, c)
    }
}

If you want to traverse the tree in parallel you will need to make further changes:

package main

import (
	&quot;code.google.com/p/go-tour/tree&quot;
	&quot;fmt&quot;
	&quot;sync&quot;
)

// 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 &lt;- 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(&quot;%d &quot;, 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 &quot;golang.org/x/tour/tree&quot;
import &quot;fmt&quot;

// 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 &lt;- 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 := &lt;-tc1;
	    x2, y2 := &lt;-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.)

huangapple
  • 本文由 发表于 2012年12月29日 04:29:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/14075382.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定