需用 sync.WaitGroup 确保测试等待所有 goroutine 完成:启动前 wg.Add(n),每个 goroutine 结尾 defer wg.Done(),测试末尾 wg.Wait();避免依赖 time.Sleep;验证并发可配合带缓冲 channel 统一收发信号。

如何用 testing 包验证 goroutine 是否真正并发执行
单纯启动多个 go func() {}() 并不保证并发行为被测试捕获——主线程可能在 goroutine 执行前就退出。关键是要让测试等待所有 goroutine 完成,同时避免竞态误判。
推荐组合使用 sync.WaitGroup 和 time.Sleep(仅用于调试)或更可靠的信号同步:
-
WaitGroup.Add(n)在启动 goroutine 前调用,defer wg.Done()在每个 goroutine 结尾调用 - 测试函数末尾调用
wg.Wait()阻塞直到全部完成 - 绝对不要依赖
time.Sleep作为主要同步手段,它不可靠且拖慢测试 - 若需验证「同时发生」,可用带缓冲的
chan struct{}让所有 goroutine 发送一次信号,再统一检查接收数量
用 channel 检测 goroutine 间数据传递是否正确
channel 是 goroutine 通信的核心载体,测试重点不是「有没有发」,而是「发得准不准、收得全不全」。
常见错误包括:向已关闭的 channel 发送、从已关闭且无数据的 channel 接收、漏收或重复收。实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 发送端用
close(ch)显式关闭(而非仅让 goroutine 退出),接收端用val, ok := 判断是否关闭 - 测试多接收场景时,用
for range ch自动处理关闭,但需确保发送端确实关闭了 channel - 对带缓冲 channel,注意
len(ch)返回当前队列长度,cap(ch)返回容量,二者差值才是剩余空间 - 避免在测试中用
select配合default做非阻塞收发——这会掩盖阻塞问题,应让测试暴露死锁
func TestChannelSendReceive(t *testing.T) {
ch := make(chan int, 2)
go func() {
ch <- 1
ch <- 2
close(ch) // 必须关闭,否则 range 会永远阻塞
}()
var received []int
for v := range ch {
received = append(received, v)
}
if len(received) != 2 || received[0] != 1 || received[1] != 2 {
t.Errorf("expected [1 2], got %v", received)
}}
如何发现并复现 data race(竞态条件)
Go 的 -race 检测器是唯一可靠手段,静态分析或人工 review 几乎无法覆盖所有路径。
启用方式简单,但容易忽略细节:
- 运行测试时加参数:
go test -race,不是go run -race(后者不生效) - 所有涉及共享变量的 goroutine,读写都必须加锁或通过 channel 同步;哪怕只是
counter++这种操作,在并发下也是未定义行为 - 切片、map、struct 字段都可能成为竞态源——例如多个 goroutine 同时向同一 slice 追加元素,即使用了
append也会触发 race 报告 - 测试中若用
time.Sleep强行制造交错执行,反而可能掩盖 race(因为调度变慢),应依赖-race自动检测
测试超时与 goroutine 泄漏的硬性检查点
goroutine 泄漏不会立刻报错,但会导致测试进程 hang 住或内存持续增长。Go 测试默认有 10 分钟超时,但应主动设更短的约束。
两个关键动作必须做:
- 给测试加
t.Parallel()时,确保没有全局状态污染;否则并发测试间会相互干扰 - 用
context.WithTimeout包裹 goroutine 启动逻辑,尤其涉及网络、文件、channel 等可能阻塞的操作 - 测试结束后,可通过
runtime.NumGoroutine()快速比对前后数量,若明显增多,大概率存在泄漏 - 注意:
http.DefaultClient等全局对象发起的请求,其底层 goroutine 可能延迟退出,需显式调用CloseIdleConnections()
并发测试里最麻烦的从来不是语法,而是「你以为它结束了,其实它还在跑」——每次加新 goroutine,都要问一句:谁关 channel?谁调 Done()?谁负责回收?










