
本文介绍一种基于通道关闭机制的可靠方法,使大量 goroutine 尽可能同时开始执行,适用于性能测试、并发基准对比等需强同步启动的场景。
本文介绍一种基于通道关闭机制的可靠方法,使大量 goroutine 尽可能同时开始执行,适用于性能测试、并发基准对比等需强同步启动的场景。
在 Go 中,“让多个 goroutine ‘同时’启动”本质上是一个同步协调问题,而非时间精度问题。由于操作系统调度、GOMAXPROCS 限制及 CPU 核心数约束,严格意义上的“纳秒级完全并行启动”在现实运行时中不可保证;但我们可以实现语义上的一致就绪与统一触发——即所有 goroutine 均已创建并阻塞等待指令,仅待一个信号便立即开始执行。这是实践中最接近“同时启动”的可靠方案。
核心思路是:利用无缓冲 channel(chan struct{})作为同步栅栏(barrier)。所有 goroutine 启动后立即尝试从该 channel 接收值(<-start),因 channel 未关闭且无数据,它们将集体阻塞;主 goroutine 在启动全部 worker 后调用 close(start),此时所有接收操作瞬间完成,goroutine 随即解除阻塞并执行后续逻辑。
以下是修正后的完整示例代码:
package main
import (
"flag"
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
routines := flag.Int("runs", 100, "number of goroutines to launch")
flag.Parse()
start := make(chan struct{}) // 同步栅栏:所有 goroutine 将在此阻塞
wg.Add(*routines)
for i := 0; i < *routines; i++ {
go func() {
<-start // 阻塞等待启动信号
defer wg.Done()
t := time.Now()
fmt.Printf("Goroutine %d started at %s\n", i, t.Format("15:04:05.000000000"))
}()
}
fmt.Println("All goroutines launched — waiting for start signal...")
close(start) // 一次性释放所有阻塞的 goroutine
fmt.Println("Signal sent. Waiting for completion...")
wg.Wait()
}⚠️ 注意事项:
- 避免变量捕获陷阱:原示例中 i 在闭包中被共享,会导致所有 goroutine 打印相同索引值。实际使用中应传参(如 go func(id int))或在循环内定义局部变量。
- channel 类型选择:chan struct{} 是最佳选择——零内存开销、语义清晰(仅作信号用途),且 close() 后所有 <-ch 操作立即返回,无竞态风险。
- 不依赖 time.Sleep 或轮询:此类方式无法保证精度,且增加不确定性与资源浪费。
- 性能提示:若 goroutine 数量远超 GOMAXPROCS,部分协程将在后续被调度器轮转执行,但其“逻辑起点”仍由 close(start) 统一界定,满足绝大多数同步启动需求(如压测起始时刻对齐)。
总结而言,通过 close(chan struct{}) 触发批量 goroutine 解阻塞,是 Go 生态中简洁、高效、无竞态、符合语言惯用法的标准解法。它不追求物理时钟的绝对同步,而是提供可预测、可复现、高一致性的并发启动语义。


















