Go- How to close channels in recursive function

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

Go- How to close channels in recursive function

问题

我是你的中文翻译助手,以下是你提供的代码的翻译:

我刚开始学习Go语言,并且正在尝试使用通道(channels)和goroutines。我想使用不同的goroutines递归遍历二叉树,但只能打印出第一个值,并且不明白为什么。如果我不关闭正在使用的通道,所有的值都会打印出来,但会出现线程锁定问题。我还尝试使用两个不同的通道,但也无法使其正常工作。希望能得到帮助。

package main

import "golang.org/x/tour/tree"
import "fmt"

// Walk函数通过通道resCh将树t中的所有值发送到通道中。
func Walk(t *tree.Tree, resCh chan int) {
	
	resCh <- t.Value
	
	if t.Left == nil && t.Right == nil {
		return
	}

	if t.Left != nil {
		go Walk(t.Left, resCh)
	}

	if t.Right != nil {
		go Walk(t.Right, resCh)
	}
}

func main() {
	resCh := make(chan int, 10)
	Walk(tree.New(1), resCh)
	<-resCh
	close(resCh)
	for i := range resCh {
		fmt.Println(i)
	}
}

希望对你有所帮助!

英文:

I am new to Go an experimenting with channels and goroutines. I want to traverse a binary tree recursively using different goroutines, but can only get the first value to print out, and don't understand why. If I don't close the channel I'm using, all values print out, but then I get a threadlock issue. I also tried using two different channels but couldn't make that work either. Any help would be appreciated.

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 resCh.
func Walk(t *tree.Tree, resCh chan int) {
	
	resCh &lt;- t.Value
	
	if t.Left==nil &amp;&amp;  t.Right==nil {
		return
	}

	if t.Left != nil {
		go Walk(t.Left, resCh)
	}

	if t.Right!=nil {
		go Walk(t.Right, resCh)
	}
}

func main() {
	resCh:= make(chan int, 10)
	Walk(tree.New(1), resCh)
	&lt;-resCh
	close(resCh)
	for i := range resCh {
		fmt.Println(i)
	}
}

答案1

得分: 1

让我先说一声谢谢你提供一个最小的重现示例。很多人在尝试解决问题时都没有包含这样的示例。你的示例中只有一个小问题,就是你没有明确指出这两行代码中的其中一行:

<-resCh
close(resCh)

或者这个代码块应该被包含,但不能同时包含:

for i := range resCh {
    fmt.Println(i)
}

如果没有close(resCh),会发生死锁,因为range操作符最终会阻塞等待更多的数据。由于没有其他正在运行的goroutine,Go运行时会注意到程序既不会终止也不会执行任何进一步的工作。因此会出现死锁错误。解决方法是在Walk函数的底部关闭通道。你还应该在一个单独的goroutine中运行Walk,而不是依赖于通道能够缓冲足够的数据以避免引发不同类型的死锁。

由于递归调用实际上并不执行任何工作,除非你真的希望引入不确定性,否则不需要在新的goroutine中运行它们。

下面是一个基本保留了你原始解决方案结构的解决方案。显然,根据所需的遍历类型,还有其他方法可以实现。

package main

import (
	"fmt"

	"golang.org/x/tour/tree"
)

// WalkBranch遍历树t,将所有值发送到通道resCh。
func WalkBranch(t *tree.Tree, resCh chan int) {

	resCh <- t.Value

	if t.Left == nil && t.Right == nil {
		return
	}

	if t.Left != nil {
		WalkBranch(t.Left, resCh)
	}

	if t.Right != nil {
		WalkBranch(t.Right, resCh)
	}
}

func Walk(t *tree.Tree, resCh chan int) {
	WalkBranch(t, resCh)
	close(resCh)
}

func main() {
	resCh := make(chan int, 2)
	go Walk(tree.New(1), resCh)
	for i := range resCh {
		fmt.Println(i)
	}
}

希望对你有帮助!

英文:

Let me start by saying thank you for a minimal reproducer. Far too many people don't include such an example of what they're trying to do. The one tiny problem with your example was you didn't make it crystal clear that either these two lines

&lt;-resCh
close(resCh)

or this block should be included but not both:

for i := range resCh {
    fmt.Println(i)
}

Without the close(resCh) a deadlock occurs because the the range operator will eventually block waiting for more data. Since there are no other running goroutines the Go runtime notices that the program will neither terminate nor do any further work. Hence the deadlock error. The solution is to close the channel at the bottom of the Walk function. You should also run Walk in a separate goroutine rather than depend on the channel being able to buffer enough data to keep from causing a different type of deadlock.

Since the recursive calls don't actually do any work you don't need to run them in new goroutines unless you really want the indeterminism that introduces.

Below is one solution that mostly retains the structure of your original solution. Obviously there are other ways of doing this depending on the type of traversal desired.

package main

import (
	&quot;fmt&quot;

	&quot;golang.org/x/tour/tree&quot;
)

// Walk walks the tree t sending all values
// from the tree to the channel resCh.
func WalkBranch(t *tree.Tree, resCh chan int) {

	resCh &lt;- t.Value

	if t.Left == nil &amp;&amp; t.Right == nil {
		return
	}

	if t.Left != nil {
		WalkBranch(t.Left, resCh)
	}

	if t.Right != nil {
		WalkBranch(t.Right, resCh)
	}
}

func Walk(t *tree.Tree, resCh chan int) {
	WalkBranch(t, resCh)
	close(resCh)
}

func main() {
	resCh := make(chan int, 2)
	go Walk(tree.New(1), resCh)
	for i := range resCh {
		fmt.Println(i)
	}
}

huangapple
  • 本文由 发表于 2023年7月19日 07:32:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/76717094.html
匿名

发表评论

匿名网友

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

确定