这段集中式同步屏障伪代码存在什么实现错误?
集中式同步屏障伪代码逻辑问题分析
先明确基础信息:这段伪代码的目标是实现集中式线程同步屏障,要求所有调用屏障接口的线程全部到达同步点后,才能集体继续向后执行。其中P是参与同步的线程总数,fetch_and_decrement是原子操作,会原子性完成“对传入地址的数值减1、返回减操作前的旧值”两个动作。
原伪代码如下:
global (shared) count : integer := P procedure central_barrier if fetch_and_decrement(&count) == 1 count := P else repeat until count == P
这段实现存在多个本质性的逻辑缺陷,根本无法保证屏障的正确性,具体问题如下:
- 没有区分屏障的“到达阶段”和“离开阶段”,连续调用屏障会直接触发死锁
这是最致命的问题。实际使用中屏障几乎都会被放在循环中反复调用,而这段代码的逻辑是:最后一个到达屏障的线程刚把count重置为P,其他自旋等待的线程读到count等于P就会立刻退出屏障,没有任何机制保证所有线程都真的完成了本轮同步再进入下一轮。
举个很容易复现的故障场景:假设总线程数P=3,三个线程需要循环执行任务+屏障同步:- 第一轮同步:线程A、B先到达屏障,两次原子递减后count值变为1,两个线程进入自旋,等待count被重置为3。
- 线程C最后到达屏障,原子递减count后拿到返回值1,开始执行
count := P把count重置为3。 - 重置刚完成,线程A就自旋读到了count=3,立刻退出第一轮屏障,跑完下一段任务后直接进入第二轮屏障调用,执行
fetch_and_decrement把刚被重置为3的count又减成了2。 - 此时还在第一轮自旋等待count=3的线程B,永远等不到count回到3,直接永久卡死。
- 缺少内存可见性保障,自旋线程可能永远读不到更新后的count值
自旋循环里对count的读取是普通读操作,在多处理器系统中,如果没有插入内存屏障强制做缓存一致性同步,等待线程可能一直读取自己本地缓存里的旧count值,哪怕最后一个到达的线程已经把count更新为P,等待线程也感知不到,一直空转死锁。 - count重置操作不是原子操作,可能读到无效中间值
最后一个线程执行的count := P是普通赋值,不是原子操作(比如在32位系统上操作64位整数时,赋值会被拆成两个写操作),自旋的线程可能刚好读到赋值过程中产生的无意义中间值,导致判断逻辑异常。
内容的提问来源于stack exchange,提问作者Zahra Heydari
相关产品推荐
相关产品推荐

