章节

10.5 协程超时处理

本篇学习使用 select 和 time.After 处理 goroutine 超时,并能避免无限等待。

协程超时处理

概念说明

并发任务可能因为网络、IO 或业务逻辑迟迟不返回。
如果调用方一直等待,程序就容易卡住。

常见做法是让任务结果通过 channel 返回,再用 select 同时等待“结果”和“超时信号”。

语法/规则

  1. 用结果 channel 接收 goroutine 的执行结果。
  2. time.After(duration) 创建超时信号。
  3. select 谁先到就处理谁。
  4. 真实项目中也常用 context.WithTimeout 统一控制取消。
  5. 超时后仍要考虑后台 goroutine 是否会泄漏。

超时处理示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import (
	"fmt"
	"time"
)

func main() {
	result := make(chan string, 1) // 带 1 个缓冲,超时后发送方也不容易被卡住

	go func() {
		time.Sleep(200 * time.Millisecond) // 模拟耗时任务,200ms 后才完成
		result <- "success"                // 任务完成后,把结果发送到 result
	}()

	select { // 同时等待“任务结果”和“超时信号”
	case msg := <-result:
		fmt.Println(msg) // 如果先拿到结果,就输出结果
	case <-time.After(100 * time.Millisecond):
		fmt.Println("timeout") // 如果 100ms 先到了,就进入超时分支
	}
}

输出结果:

1
timeout

这段 select 是怎么运行的

先看这两个等待对象:

1
2
case msg := <-result
case <-time.After(100 * time.Millisecond)

可以先理解成:

1
2
等待 1goroutine 什么时候把结果发到 result
等待 2100ms 什么时候到

再看 goroutine:

1
2
3
4
go func() {
	time.Sleep(200 * time.Millisecond)
	result <- "success"
}()

它的意思是:

1
200ms result 才会收到 "success"

所以时间顺序就是:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
0ms:
进入 select
同时等待 result  time.After(100ms)

100ms:
time.After(100ms) 先到
进入 case <-time.After(100 * time.Millisecond)
输出 timeout

200ms:
goroutine 才准备把 "success" 发到 result
但前面的 select 已经结束了

所以这里进入超时分支,不是随机的。
而是因为:

1
200ms > 100ms

结果还没来,超时信号先到了,所以执行:

1
2
case <-time.After(100 * time.Millisecond):
	fmt.Println("timeout")

常见错误

  1. 只等待结果 channel,没有设置超时,导致调用方可能一直阻塞。
  2. 使用无缓冲结果 channel,超时后后台 goroutine 发送结果时可能被卡住。
  3. 认为超时分支执行后 goroutine 会自动停止,实际它仍可能继续运行。
本文禁止转载
使用 Hugo 构建
主题 StackJimmy 设计 由 Hobin 魔改
最近构建时间:2026-04-17 19:07:48 CST
载入天数...载入时分秒...
发表了 1 篇文章 · 发表了 152 篇笔记 · 总计 18 万 0 千字