对 Go 语言中循环屏障 CyclicBarrier 的理解

        同步屏障 (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 到达同一个检查点” 的场景

### Android 系统中的三种线程同步方法 #### 1. 使用 `synchronized` 关键字 `synchronized` 是 Java 提供的一种内置锁机制,能够确保同一时刻只有一个线程可以访问被标记为 `synchronized` 的代码块或方法。当一个线程进入某个对象的一个 `synchronized` 方法时,它就获得了这个对象上的锁;直到该线程离开此方法之前,其他任何试图访问相同对象上任意 `synchronized` 成员函数的线程都必须等待。 ```java public class Counter { private int count; public synchronized void increment() { this.count++; } } ``` 这种方式虽然简单易用,但是性能相对较低,因为每次都需要获取和释放锁,在多核处理器环境下可能会造成不必要的上下文切换开销[^2]。 #### 2. 利用 `Volatile` 变量保证可见性和有序性 对于某些特定场景下的共享资源更新问题,如果只是简单的赋值操作而不需要复杂的逻辑控制,则可以直接采用 `volatile` 来修饰字段。这使得对该变量的所有读取都将直接从主内存中获取最新值而不是本地缓存副本,并且编译器不会对其指令重排序优化从而破坏程序语义一致性。 ```java private volatile boolean flag = false; // 当前线程设置标志位 flag = true; // 其他线程检测到变化立即响应处理... if (flag) { // do something } ``` 需要注意的是,仅靠 `volatile` 并不能解决所有的并发安全问题,比如复合动作(如先检查再修改)仍然存在竞态条件风险[^3]。 #### 3. 借助高级工具类实现复杂业务需求 Java 并发包 (`java.util.concurrent`) 中包含了多种实用的数据结构和服务来简化开发人员编写高效可靠的并行应用程序的工作: - **CountDownLatch**: 计数信号量,允许一个或多个线程一直等到一组事件发生之后才继续执行。 - **CyclicBarrier**: 循环屏障,可以让固定数量的参与者聚集在一起共同启动某项活动,完成后自动复位以便下一轮使用。 - **Semaphore**: 限流计数器,用于管理对有限资源池(例如数据库连接)的同时访问权限数目限制。 - **Exchanger<V>**: 数据交换站,两个线程可以在指定位置互换它们各自持有的数据实例。 以 `CountDownLatch` 为例展示如何协调不同任务间的协作关系: ```java import java.util.concurrent.CountDownLatch; class Worker implements Runnable { private final CountDownLatch startSignal; private final CountDownLatch doneSignal; Worker(CountDownLatch startSignal, CountDownLatch doneSignal) { this.startSignal = startSignal; this.doneSignal = doneSignal; } @Override public void run() { try { System.out.println(Thread.currentThread().getName()+" is ready"); startSignal.await(); // wait until all threads are ready doWork(); System.out.println(Thread.currentThread().getName()+" has finished work"); doneSignal.countDown(); // signal that the current thread has completed its job } catch (InterruptedException e) {} } void doWork(){} } // Usage example: int n = 5; // number of workers CountDownLatch startSignal = new CountDownLatch(n); CountDownLatch doneSignal = new CountDownLatch(n); for(int i=0;i<n;++i){ new Thread(new Worker(startSignal,doneSignal)).start(); } System.out.println("All threads have been started."); try{ startSignal.countDown(); // let them go doneSignal.await(); // wait for completion }catch(Exception ex){} System.out.println("All works were done!"); ``` 上述例子展示了利用 `CountDownLatch` 控制一批工作单元按顺序依次开始运行以及全部结束后的通知过程。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mindfulness code

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值