You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go递归函数中通道关闭问题:二叉树协程遍历异常求助

Go二叉树遍历:goroutine与通道的问题解决

问题出在哪

你当前的代码有两个核心问题:

  1. 通道过早关闭:main里调用Walk后,刚取出第一个值就关闭了通道。这时候启动的子goroutine还没来得及把剩余节点值发送到通道,甚至后续发送操作会因为通道关闭直接panic。
  2. 不关闭通道导致死锁:如果不手动关闭通道,for range会一直阻塞等待新数据,所有遍历goroutine执行完后没有关闭通道的动作,main goroutine就会因为永远等不到通道关闭而死锁。

修复后的代码

用sync.WaitGroup追踪所有遍历goroutine的执行状态,等所有节点遍历完成后再关闭通道:

package main

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

// Walk walks the tree t sending all values
// from the tree to the channel resCh.
func Walk(t *tree.Tree, resCh chan int, wg *sync.WaitGroup) {
    defer wg.Done() // 当前goroutine执行完后,标记任务完成
    resCh <- t.Value

    if t.Left != nil {
        wg.Add(1) // 新增遍历左子树的goroutine,计数+1
        go Walk(t.Left, resCh, wg)
    }

    if t.Right != nil {
        wg.Add(1) // 新增遍历右子树的goroutine,计数+1
        go Walk(t.Right, resCh, wg)
    }
}

func main() {
    resCh := make(chan int, 10)
    var wg sync.WaitGroup

    wg.Add(1) // 主遍历goroutine计数+1
    go Walk(tree.New(1), resCh, &wg)

    // 单独开goroutine等所有遍历完成后关闭通道
    go func() {
        wg.Wait()
        close(resCh)
    }()

    // 遍历通道打印所有节点值
    for val := range resCh {
        fmt.Println(val)
    }
}

核心改动说明

  • WaitGroup追踪goroutine:每启动一个递归遍历的goroutine就调用wg.Add(1),每个goroutine结束前用defer wg.Done()减少计数,确保所有遍历任务都被监控。
  • 延迟关闭通道:专门启动一个goroutine等待wg.Wait()(所有遍历完成),然后关闭通道。这样main里的for range就能完整遍历所有节点值,遍历结束后自动退出,不会死锁。
  • 主Walk转为goroutine:把初始的Walk调用包装成goroutine,避免阻塞main,保证遍历逻辑全在goroutine中并行执行。

内容的提问来源于stack exchange,提问作者Skwidman396

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 12:35:20