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

如何用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 05:06:34