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

Go语言中如何标记通道值已读取处理,以正确退出无限循环?

解决方案

问题根源

  1. counter提前递增:原代码读取通道后立刻执行counter++,但平方计算的goroutine是异步执行的,导致counter先达到阈值退出循环,此时可能还有任务未处理完成。
  2. 死锁问题:原代码用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 19:10:29