我是第一次try 频道和Goroutine.我想使用不同的Goroutine递归地遍历一棵二叉树,但只能得到要打印的第一个值,并且不明白为什么.如果我不关闭我正在使用的通道,所有的值都会打印出来,但随后会出现线程锁定问题.我也试过使用两种不同的渠道,但也不能奏效.任何帮助都将不胜感激.

package main

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

// Walk walks the tree t sending all values
// from the tree to the channel resCh.
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)
    }
}

推荐答案

首先,让我说声谢谢你给我一个最小的复制者.太多的人不包括他们试图做的事情的这样一个例子.你的例子有一个小问题,那就是你没有清楚地说明这两行

<-resCh
close(resCh)

或者应该包括此块,但不能同时包括这两个块:

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

如果没有close(resCh),则会发生死锁,因为range运算符将最终阻止等待更多数据.由于没有其他正在运行的Goroutine,Go运行时会通知程序既不会终止,也不会做任何进一步的工作.因此出现了死锁错误.解决方案是关闭Walk功能底部的通道.您还应该在单独的Goroutine中运行Walk,而不是依赖于通道能够缓冲足够的数据以避免导致不同类型的死锁.

因为递归调用实际上不做任何工作,所以您不需要在新的Goroutine中运行它们,除非您真的想要引入的不确定性.

下面是一个解决方案,它基本上保留了原始解决方案的 struct .显然,根据所需的遍历类型,还有其他方法可以做到这一点.

package main

import (
    "fmt"

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

// Walk walks the tree t sending all values
// from the tree to the channel 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)
    }
}

Go相关问答推荐

如何在Golang中覆盖404

迭代字符串并用映射值替换原始字符串中的值的惯用方法

Redis:尽管数据存在,但 rdb.Pipelined 中出现redis:nil错误

用接口来模拟amqp091go的困难

Golang和Gin web框架在router.Run()之后执行代码

Secrets Manager Update Secret - Secret String 额外的 JSON 编码

当我的 go build 成功时,如何修复我的 docker build 失败? Dockerfile 包括 go mod 下载

如何在 Golang 中打印 2 列表?

在反向 GORM 中创建查询有一个关系

Wire google Inject with multi return from provider 函数

在 Go 模板中对照片使用随机 Int

为什么 reflect.TypeOf(new(Encoder)).Elem() != reflect.TypeOf(interfaceVariable)?

Golang模板无法访问embedFS中的文件

在删除级联时无法在 Gorm 中按预期工作

将接口方法的参数限制为几个允许的 struct ?

Golang SSH客户端错误无法验证,try 的方法[无公钥],没有支持的方法

为什么 go.mod 中的所有依赖都是间接的?

如何在 Unmarshal 中使用泛型(转到 1.18)

如何在 Prometheus 中正确检测区域和环境信息?

Go 语言的select语句