正确做法是用 sync.WaitGroup 显式等待:wg.Add(1) 在 go 前调用,defer wg.Done() 放 goroutine 首行,wg.Wait() 在主协程中且仅调用一次;sync.Mutex 必须手动声明并配对使用 defer 加锁/解锁;并发测试需手动添加多 goroutine 压测。

用 sync.WaitGroup 控制 goroutine 生命周期,避免主协程提前退出
Go 程序里一加 go 就跑,但主协程结束,所有子 goroutine 会直接被杀掉——你看到的“没输出”“结果为空”,大概率不是逻辑错,是主协程跑太快了。别用 time.Sleep 硬等,它不可靠、难维护、测试不稳。
正确做法是用 sync.WaitGroup 显式声明“我等这几个任务做完再走”:
-
wg.Add(1)必须在go语句前调用,且不能漏; -
defer wg.Done()放在 goroutine 函数体第一行最安全,防止中间 panic 或 return 漏掉; -
wg.Wait()必须在主 goroutine 中调用,且只能调一次,重复调用会 panic。
结构体字段初始化时自动加 sync.Mutex 字段?别信,手动加才可控
GoLand 的 “Fill fields” 功能不会帮你加锁字段,也不会提醒你哪些字段需要保护。它只管填零值:int 填 0、map 填 nil、sync.Mutex 填空结构体(这本身合法,但容易误以为“已就绪”)。
真正要并发安全,得自己动手:
- 共享字段(如
count int、cache map[string]string)旁,显式声明mu sync.Mutex或rw sync.RWMutex; - 读操作用
rw.RLock()+defer rw.RUnlock(); - 写操作用
rw.Lock()+defer rw.Unlock(); - 别把
Lock()和Unlock()写在 if 分支里——容易漏,一律 defer。
生成测试代码时,别只测逻辑,要测并发竞争
GoLand 的 Generate test(Alt+Insert → Test for function)默认只生成单 goroutine 测试。但并发安全问题只在多 goroutine 同时读写时暴露。
GoLand 2026.1.1 是 2026.1 发布后的首个维护修正版本,适合已经开始体验 2026.1 新功能并希望同步补丁的开发者。它更适合用于入门项目、现有项目迁移测试和 IDE 行为验证。
手动补上并发压测片段:
- 用
for i := 0; i 启多个 goroutine 调同一函数; - 用
sync.WaitGroup等它们全结束; - 检查最终状态是否符合预期(比如计数器是否等于 100);
- 加上
-race标志运行:go test -race,它能抓到绝大多数数据竞争。
sync.Once 和 sync.Map 不是万能钥匙,该用锁时别绕路
看到 “并发安全” 就想套 sync.Map?小心掉坑里。它只适合读多写少、键值对生命周期长、且不需要遍历的场景。一旦你要 range、要 len()、要原子性地删+查,sync.Map 就不如带 sync.RWMutex 的普通 map 直观可靠。
sync.Once 同理:它只保“初始化一次”,不保“后续访问安全”。比如你用 once.Do(initCache) 初始化了一个 map,之后所有 goroutine 还是得自己加锁读写这个 map。
真正容易被忽略的点是:锁的粒度。一个 sync.Mutex 锁整个 struct 很省事,但可能成为性能瓶颈;拆成字段级锁又容易漏。没有银弹,得看实际访问模式——先用 -race 跑出问题,再针对性加锁。

















