
通过关闭一个无缓冲 channel 作为全局启动信号,可让所有已创建但尚未执行的 goroutine 几乎在同一时刻开始运行,这是 go 中实现协同式并发启动的标准实践。
通过关闭一个无缓冲 channel 作为全局启动信号,可让所有已创建但尚未执行的 goroutine 几乎在同一时刻开始运行,这是 go 中实现协同式并发启动的标准实践。
在 Go 中,“让多个 goroutine 同时启动”本质上是指:所有 goroutine 完成创建后,统一、同步地进入实际工作逻辑。由于操作系统调度、GOMAXPROCS 限制及 CPU 核心数等客观因素,严格意义上的“精确同一纳秒执行”无法保证;但我们可以做到高度一致的启动时机——即所有 goroutine 在 close(start) 调用后几乎立即解除阻塞并开始执行。
实现的关键在于:使用一个未关闭的 channel 让所有 goroutine 先阻塞等待,再由主线程统一关闭该 channel 触发集体唤醒。close(ch) 对于已接收 <-ch 的 goroutine 是瞬时、无竞争、无延迟的唤醒机制,远比 time.Sleep 或 sync.WaitGroup 等方式更精准可靠。
以下是完整可运行的示例代码:
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{}) // 无缓冲 channel,用于同步启动
wg.Add(*routines)
for i := 0; i < *routines; i++ {
go func(id int) {
<-start // 所有 goroutine 在此阻塞,等待统一指令
defer wg.Done()
t := time.Now()
fmt.Printf("Goroutine %d started at %s\n", id, t.Format("15:04:05.000"))
}(i)
}
fmt.Println("All goroutines launched — waiting for start signal...")
time.Sleep(100 * time.Millisecond) // 可选:确保所有 goroutine 已调度并阻塞在 <-start 上
fmt.Println("Broadcasting start signal...")
close(start) // 关键:一次性唤醒全部 goroutine
wg.Wait()
fmt.Println("All done.")
}? 注意事项与最佳实践:
- ✅ 必须使用 make(chan struct{})(无缓冲 channel),避免因缓冲导致部分 goroutine 提前“偷跑”;
- ✅ close(start) 是线程安全的,且仅需调用一次;多次关闭会 panic,因此务必确保只执行一次;
- ✅ 若需传递启动参数(如 ID、配置),请通过闭包捕获或显式传参(如示例中 func(id int)),避免循环变量 i 的常见陷阱;
- ⚠️ 不要依赖 time.Sleep 来“等待 goroutine 就位”——它不可靠且不具可移植性;实际生产中,只要 goroutine 创建完成并执行到 <-start,即可认为已就绪;
- ? 可通过 runtime.GOMAXPROCS(n) 和 runtime.NumCPU() 辅助评估并发上限,但启动同步逻辑本身与 GOMAXPROCS 无关。
总结来说,channel + close() 是 Go 生态中实现 goroutine 协同启动的惯用、高效且符合语言哲学的方式。它轻量、无锁、语义清晰,是编写可控并发程序的重要基础技巧。


















