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

遍历列表时如何为并发调用设置超时并丢弃超时结果

问题分析与解决方案

原代码的核心问题

  1. 结果无条件存入通道:results通道的缓冲容量等于任务总数(10),所有goroutine执行results <- doSomething(k)时不会阻塞,不管超时与否,所有结果都会被提前存入通道。因此即使触发超时,遍历通道仍会拿到全部结果。
  2. 超时逻辑失效:超时触发后,未完成的goroutine仍会继续执行并发送结果,因为没有信号通知它们停止发送。

修正方案

我们需要让每个任务仅在超时前将结果发送到通道,超时后的结果直接丢弃。使用context实现超时信号广播是最优雅的方式:

package main

import (
    "context"
    "fmt"
    "sync"
    "time"
)

func doSomething(x int) int {
    time.Sleep(time.Second * 2)
    return x
}

func main() {
    timeoutCfg := 5
    somethings := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    results := make(chan int, len(somethings))
    var wg sync.WaitGroup

    // 创建带超时的上下文,超时后自动取消
    ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(timeoutCfg))
    defer cancel() // 确保上下文最终被取消,避免资源泄漏

    for _, key := range somethings {
        wg.Add(1)
        go func(k int) {
            defer wg.Done()
            res := doSomething(k)
            // 尝试发送结果,同时监听超时信号
            select {
            case results <- res:
                // 发送成功,结果被保留
            case <-ctx.Done():
                // 已超时,直接丢弃结果并退出
                return
            }
        }(key)
    }

    // 等待所有goroutine完成后关闭结果通道
    go func() {
        wg.Wait()
        close(results)
    }()

    var collected []int
    select {
    case <-ctx.Done():
        fmt.Println("timeout")
        // 收集通道中已有的结果,避免阻塞
        for {
            select {
            case res, ok := <-results:
                if !ok {
                    break
                }
                collected = append(collected, res)
            default:
                // 通道中无更多结果,结束收集
                goto endCollect
            }
        }
    default:
        // 所有任务在超时前完成,收集全部结果
        fmt.Println("done")
        for res := range results {
            collected = append(collected, res)
        }
    }
endCollect:

    fmt.Println("保留的结果:")
    for _, x := range collected {
        fmt.Println(x)
    }
}

关键改进点

  • 超时信号广播:通过context.WithTimeout创建超时上下文,所有goroutine监听ctx.Done()信号,超时后自动停止发送结果。
  • 结果发送控制:每个goroutine完成任务后,用select尝试发送结果,若已超时则直接丢弃,确保只有超时前完成的结果被保留。
  • 安全收集结果:超时触发后,通过select的default分支避免在结果通道上阻塞,只收集已发送的结果。

内容的提问来源于stack exchange,提问作者sudeepgupta90

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 17:05:10