sync.WaitGroup适用于等待所有goroutine完成,需在启动前调用Add,内部用原子计数器跟踪任务数,Wait阻塞至计数归零;不可复制、禁止Add与Wait并发、推荐defer wg.Done()确保执行。

Go里没有“让goroutine按顺序执行”的开关,只有靠同步原语显式编排时序。想让多个任务串行、有依赖、或关键步骤不交错,必须选对工具——用错就卡死、漏等、或根本没效果。
为什么go f()之后直接fmt.Println总打印空值
因为go启动的是异步协程,主goroutine不会停;fmt.Println执行时,f()大概率还没跑完,甚至程序已退出。这不是延迟问题,是缺少同步信号。
-
sync.WaitGroup适合“等全部做完”:先wg.Add(2),再go f1(&wg)和go f2(&wg),最后wg.Wait() -
chan struct{}适合“等某一个做完”:done := make(chan struct{}),goroutine末尾done ,主goroutine<code>阻塞等待 - 别在
main函数末尾直接return,否则所有未完成的goroutine会被强制终止
sync.WaitGroup的Add必须在go之前调用
不是语法限制,而是内存模型要求:wg.Add()要先于goroutine启动,否则可能因竞态导致计数漏加或重复加。Go不保证go f(wg)中wg参数传递与wg.Add()的执行顺序。
- 正确写法:
wg.Add(1); go task(&wg) - 错误写法:
go func() { wg.Add(1); task(&wg) }()—— 这会引发竞态,Wait()可能永远不返回 -
Done()必须在goroutine内调用,且只能调用一次;多次调用会panic
用无缓冲channel做任务队列实现FIFO串行执行
当多个地方提交任务(比如HTTP请求、定时器、后台事件),但希望它们严格按提交顺序一个一个执行,chan Task比锁更自然。
立即学习“go语言免费学习笔记(深入)”;
- 定义
type Task func(),创建taskCh := make(chan Task)(无缓冲) - 启动一个长期运行的dispatcher:
go func() { for t := range taskCh { t() } }() - 提交任务统一走
taskCh ,天然FIFO,且自动限流 - 注意:dispatcher里要加
recover(),否则某个task panic会导致整个队列卡死
多个goroutine竞争同一资源时,sync.Mutex不是首选方案
Go推崇“通过通信共享内存”,而不是“通过共享内存通信”。Mutex容易误用、遗漏Unlock、或造成不必要的串行化。
- 优先考虑能否把共享状态封装进channel:比如用
chan int做计数器,而非mu.Lock(); count++; mu.Unlock() - 若必须用锁,确保
Lock()和Unlock()成对出现在同一作用域,推荐用defer mu.Unlock() - 读多写少场景可用
sync.RWMutex,但要注意RUnlock()不能在未RLock()时调用
真正难的不是选哪个原语,而是判断“这里到底需要同步什么”——是完成信号?执行次序?还是临界区保护?错把“等待完成”写成“抢锁”,或者该用channel的地方硬套WaitGroup,都会让逻辑变脆、调试变难。


















