同步屏障 (Barrier) 是并发编程中的一种同步方法。对于一组 goroutine ,程序中的一个同步屏障意味着任何 goroutine 执行到此后都必须等待,直到所有的 goroutine 都达到此点才可继续执行下文。
Barrier 无论是翻译成屏障、障碍还是栅栏,都很形象,就是一道拦截坝,拦截一组对象,等对象齐了才打开它。
CyclicBarrier 允许一组 goroutine 彼此等待,到达一个共同的检查点,然后到达下一个同步点,循环使用。因为它可以被重复使用,所以叫做循环屏障,或者叫作可循环使用的屏障。具体的机制是,大家都在屏障前等待,等全部到齐了,就打开屏障放行。
1. CyclicBarrier 的使用场景
你可能会觉得,CyclicBarrier 和 WaitGroup 的功能有点类似。确实是这样的。不过,CyclicBarrier 更适合用在 “数量固定的 goroutine 等待到达同一个检查点” 的场景中,而且在放行 goroutine 之后,CyclicBarrier 可以被重复利用,不像 WaitGroup 被重用时必须小心翼翼,避免发生 panic。
在处理可重用的多个 goroutine 等待到达同一个检查点的场景时,CyclicBarrier 和 WaitGroup 方法调用的对应关系如下图所示:
在重复使用 WaitGroup 的时候,wg.Add 和 wg.Wait 的下一次调用并不能很好地同步,所以这也是 CyclicBarrier 擅长处理的场景。
可以看到,如果使用 WaitGroup 实现的话,调用比较复杂,不像使用 CyclicBarrier 那么清爽。更重要的是,如果想重用 WaitGroup, 还要保证将 WaitGroup 的计数值重置到 n 时不会出现并发问题。
WaitGroup 更适合用在 “一个 goroutine 等待一组 goroutine 到达同一个检查点” 的场景中,或者是不需要重用的场景中。
其实上面的区别还不是最关键的,这两个同步原语最重要的不同在于:CyclicBarrier 的参与者之间相互等待ÿ