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

Go语言中time.After未按预期触发超时?代码问题咨询

Go select超时未按预期触发的原因分析

问题描述

我编写了如下Go代码:通过goroutine每秒向通道发送一次"result",共发送10次;主函数中使用select监听通道与time.After(3秒),预期3秒后打印"timeout"并退出,但实际是等10次"result"全部打印完成后才输出"timeout",请问这是为什么?

package main
import "fmt"
import "time"

func main() {
  ch := make(chan string)
  go func() {
    for i:=0;i<10;i++ {
      time.Sleep(time.Second * 1)
      ch <- "result"
    }
      
  }()
  for {
    select {
    case res := <-ch:
        fmt.Println(res)
    case <-time.After(time.Second * 3):
        fmt.Println("timeout")
        return
    }
  }
}

运行结果:
result
result
result
result
result
result
result
result
result
result
timeout

问题原因

问题核心在于**time.After的创建位置错误**:你把time.After(time.Second *3)放在了select代码块内部,每次循环执行到select时,都会重新创建一个全新的3秒定时器。

由于goroutine每秒都会向通道发送数据,select几乎每次都会优先命中case res := <-ch分支,处理完数据后进入下一次循环,再次生成新的3秒定时器。只有当10次数据全部发送完成后,通道没有新数据可读,select才会等待这次新创建的定时器触发——此时距离程序启动已经过了10秒,所以最后才打印"timeout"。

修复方案

将time.After的结果提前保存到变量中,让整个循环复用同一个定时器,确保3秒的计时从程序启动时开始计算:

package main
import "fmt"
import "time"

func main() {
  ch := make(chan string)
  go func() {
    for i:=0;i<10;i++ {
      time.Sleep(time.Second * 1)
      ch <- "result"
    }
  }()
  
  // 提前创建定时器,复用同一个超时通道
  timeout := time.After(time.Second * 3)
  for {
    select {
    case res := <-ch:
        fmt.Println(res)
    case <-timeout:
        fmt.Println("timeout")
        return
    }
  }
}

修改后,定时器会在程序启动3秒后准时触发,直接执行超时分支退出程序,符合预期逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 13:50:15