Go语言中如何标记通道值已读取处理,以正确退出无限循环?
解决方案
问题根源
- counter提前递增:原代码读取通道后立刻执行
counter++,但平方计算的goroutine是异步执行的,导致counter先达到阈值退出循环,此时可能还有任务未处理完成。 - 死锁问题:原代码用
defer close(ch)在main末尾关闭通道,当主goroutine读完缓冲通道的所有值后,继续读取会阻塞,而此时生产者goroutine可能还在运行,通道未关闭,最终导致死锁。
方案1:用通知通道确保处理完成后计数
通过done通道让处理goroutine完成后通知主goroutine,再递增counter,同时确保通道在所有生产者完成后关闭:
package main import ( "fmt" "sync" ) func main() { num := 5 ch := make(chan int, num) var wgProducers sync.WaitGroup // 启动生产者goroutine for i := 0; i < num; i++ { wgProducers.Add(1) go func(val int) { defer wgProducers.Done() fmt.Printf("Added value: %d to the channel\n", val) ch <- val }(i) } // 所有生产者完成后关闭通道 go func() { wgProducers.Wait() close(ch) }() counter := 0 done := make(chan struct{}) for { val, ok := <-ch if !ok { close(done) break } // 启动处理任务 go func(i int) { sq := i * i fmt.Println(sq) done <- struct{}{} // 处理完成发送信号 }(val) <-done // 等待处理完成再计数 counter++ if counter == num { break } } }
方案2:用WaitGroup简化并发控制(更符合Go习惯)
直接用range遍历通道(通道关闭后自动退出循环),用sync.WaitGroup跟踪所有处理任务,无需手动维护counter:
package main import ( "fmt" "sync" ) func main() { num := 5 ch := make(chan int, num) var wgProducers sync.WaitGroup var wgWorkers sync.WaitGroup // 生产者逻辑 for i := 0; i < num; i++ { wgProducers.Add(1) go func(val int) { defer wgProducers.Done() fmt.Printf("Added value: %d to the channel\n", val) ch <- val }(i) } // 生产者全部完成后关闭通道 go func() { wgProducers.Wait() close(ch) }() // 消费并处理任务 for val := range ch { wgWorkers.Add(1) go func(i int) { defer wgWorkers.Done() sq := i * i fmt.Println(sq) }(val) } // 等待所有处理任务完成 wgWorkers.Wait() fmt.Println("所有任务处理完成") }
内容的提问来源于stack exchange,提问作者Coldchain9
相关产品推荐
相关产品推荐

