Go并发系列:3扩展同步原语-3.5 CyclicBarrier
3.5 CyclicBarrier
在并发编程中,协调多个 Goroutine 的执行顺序是一项常见的任务。在 Go 语言中,虽然没有直接提供类似 Java 中 CyclicBarrier 的原生支持,但我们可以通过 Channel 和 WaitGroup 等原语来实现类似的功能。CyclicBarrier 的作用是使一组 Goroutine 达到某个屏障点后才继续执行。
CyclicBarrier 的基本概念
CyclicBarrier 是一个同步辅助类,允许一组 Goroutine 互相等待,直到所有 Goroutine 都到达一个公共的屏障点(barrier)。这个屏障点可以被反复使用,适合需要重复执行某个步骤的并发任务。
CyclicBarrier 的实现
我们可以使用 Go 的 Channel 和 WaitGroup 来实现一个简单的 CyclicBarrier。以下是一个示例实现:
package main
import (
"fmt"
"sync"
)
type CyclicBarrier struct {
parties int
count int
mutex sync.Mutex
condition *sync.Cond
}
func NewCyclicBarrier(parties int) *CyclicBarrier {
cb := &CyclicBarrier{
parties: parties,
count: parties,
}
cb.condition = sync.NewCond(&cb.mutex)
return cb
}
func (cb *CyclicBarrier) Await() {
cb.mutex.Lock()
defer cb.mutex.Unlock()
cb.count--
if cb.count == 0 {
cb.count = cb.parties
cb.condition.Broadcast()
} else {
cb.condition.Wait()
}
}
func main() {
const numGoroutines = 5
barrier := NewCyclicBarrier(numGoroutines)
var wg sync.WaitGroup
wg.Add(numGoroutines)
for i := 1; i <= numGoroutines; i++ {
go func(id int) {
defer wg.Done()
fmt.Printf("Goroutine %d is waiting at the barrier\n", id)
barrier.Await()
fmt.Printf("Goroutine %d has crossed the barrier\n", id)
}(i)
}
wg.Wait()
}
示例分析
在这个示例中,我们实现了一个简单的 CyclicBarrier。CyclicBarrier 的核心是一个计数器(count),以及一个条件变量(condition)。当一个 Goroutine 调用 Await
方法时,计数器会减一。如果计数器减到零,表示所有 Goroutine 都已经到达屏障点,此时重置计数器,并通知所有等待的 Goroutine 继续执行。否则,Goroutine 将等待,直到计数器减到零。
在 main
函数中,我们创建了一个包含 5 个 Goroutine 的 CyclicBarrier,并启动这 5 个 Goroutine,每个 Goroutine 到达屏障点后等待其他 Goroutine。当所有 Goroutine 都到达屏障点后,它们将继续执行。
CyclicBarrier 的使用场景
CyclicBarrier 适用于需要在多个步骤中重复执行某个任务的并发场景,例如:
- 并行处理多个任务,每个任务包含多个阶段,各阶段之间需要同步。
- 多个 Goroutine 需要在某个点上同步,然后再继续执行后续操作。
结论
通过使用 Go 的同步原语,我们可以实现类似 Java 中 CyclicBarrier 的功能。虽然 Go 没有直接提供 CyclicBarrier 类,但灵活运用 Channel 和 WaitGroup 等工具,可以实现各种复杂的并发控制。掌握这些技术,可以帮助我们编写高效、健壮的并发程序,充分发挥多核处理器的性能。
转载自:https://juejin.cn/post/7387934821185880102