Go递归函数中通道关闭问题:二叉树协程遍历异常求助
Go二叉树遍历:goroutine与通道的问题解决
问题出在哪
你当前的代码有两个核心问题:
- 通道过早关闭:
main里调用Walk后,刚取出第一个值就关闭了通道。这时候启动的子goroutine还没来得及把剩余节点值发送到通道,甚至后续发送操作会因为通道关闭直接panic。 - 不关闭通道导致死锁:如果不手动关闭通道,
for range会一直阻塞等待新数据,所有遍历goroutine执行完后没有关闭通道的动作,maingoroutine就会因为永远等不到通道关闭而死锁。
修复后的代码
用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
相关产品推荐
相关产品推荐

