如何用for/select语法从多通道接收数据并退出无限循环?
解决Go多Channel接收时的无限循环问题
你的代码陷入无限循环的核心原因是:当所有goroutine执行完毕后,WaitGroup触发关闭了sumChannel和productChannel,但已关闭的channel会持续返回对应类型的零值,select会不断命中这两个case,导致for循环永远无法退出。
下面提供两种实用的解决方案:
方案一:用计数器统计接收次数
每个compute goroutine会往两个channel各发送1条数据,5个goroutine总共会产生10条数据。我们可以在主循环中统计接收次数,达到预期数量后直接退出循环。
修改后的代码:
package main import ( "fmt" "sync" "time" ) func main() { var wg sync.WaitGroup sumChannel := make(chan int) productChannel := make(chan int) totalCount := 0 expectedCount := 5 * 2 // 5个goroutine × 每个发2条数据 for i := 1; i <= 5; i++ { wg.Add(1) go compute(i, sumChannel, productChannel, &wg) } go func() { wg.Wait() close(sumChannel) close(productChannel) }() for { select { case <-sumChannel: fmt.Println("sum") totalCount++ case <-productChannel: fmt.Println("prod") totalCount++ } if totalCount == expectedCount { break } } fmt.Println("所有数据处理完成,退出循环") } func compute(i int, sumChannel chan int, productChannel chan int, wg *sync.WaitGroup) { time.Sleep(2 * time.Second) sumChannel <- i + i productChannel <- i * i wg.Done() }
方案二:使用quit通道配合WaitGroup
新增一个quit通道,在WaitGroup等待完成后发送退出信号。同时通过val, ok := <-ch的方式判断channel是否已关闭,若关闭则将channel设为nil(select会忽略nil的channel),避免持续接收零值。
修改后的代码:
package main import ( "fmt" "sync" "time" ) func main() { var wg sync.WaitGroup sumChannel := make(chan int) productChannel := make(chan int) quit := make(chan struct{}) // 用struct{}节省内存,无需传递实际值 for i := 1; i <= 5; i++ { wg.Add(1) go compute(i, sumChannel, productChannel, &wg) } go func() { wg.Wait() close(sumChannel) close(productChannel) quit <- struct{}{} // 发送退出信号 }() for { select { case val, ok := <-sumChannel: if !ok { sumChannel = nil // 设为nil,后续select不再命中该case continue } fmt.Println("sum:", val) case val, ok := <-productChannel: if !ok { productChannel = nil // 同理处理 continue } fmt.Println("prod:", val) case <-quit: // 确认两个channel都已处理完毕再退出 if sumChannel == nil && productChannel == nil { fmt.Println("收到退出信号,所有数据处理完成") return } } } } func compute(i int, sumChannel chan int, productChannel chan int, wg *sync.WaitGroup) { time.Sleep(2 * time.Second) sumChannel <- i + i productChannel <- i * i wg.Done() }
内容的提问来源于stack exchange,提问作者neo
相关产品推荐
相关产品推荐

