我们是否需要一次性自旋锁的内存获取屏障?

问题描述 投票:0回答:1

对于锁(自旋锁、互斥锁),我们通常需要在加锁时添加acquire fence以保证锁的功能。

但这对于一次性自旋锁来说是必要的吗?例如:

int val = 0;
int lock = 0;

void thread0(void)
{
    int tmp = 0;
    if (atomic_compare_exchange_strong_explicit(&lock, &tmp, 1, memory_order_relaxed, memory_order_relaxed)) { // do we need memory_order_acquire here ?
        assert(!val); // will it always success?
        val = 1;
    }
}

// same as thread0
void thread1(void)
{
    int tmp = 0;
    if (atomic_compare_exchange_strong_explicit(&lock, &tmp, 1, memory_order_relaxed, memory_order_relaxed)) {
        assert(!val);
        val = 1;
    }
}

更具体地说,以下代码在armv7-a架构上是否正确(与上面提到的C代码可能存在一些差异):

val:
    .long 0
lock:
    .long 0

core0:
    mov r0, #val
    mov r1, #lock
    mov r4, #1
2:
    ldrex r2, [r1]
    cmp r2, #0
    beq 1f
    bx  lr  // ret
1:
    strex r3, r4, [r1]
    cmp r3, #0
    bne 2b

    // without acquire fence
    ldr r5, [r0] // is r5 != 0 allowed?



core1:
    mov r0, #val
    mov r1, #lock
    mov r4, #1
2:
    ldrex r2, [r1]
    cmp r2, #0
    beq 1f
    bx  lr  // ret
1:
    strex r3, r4, [r1]
    cmp r3, #0
    bne 2b

    dmb ish  // acquire fence
    str r4, [r0]  // store 1
c assembly arm atomic memory-barriers
1个回答
0
投票

断言总是成功,因为只有一个线程或另一个线程运行

if
主体,并且它在同一线程中的
val=1
之前排序。

原子 RMW 决定哪个线程赢得竞争,但它不需要与任何先前的编写器同步以防止其“关键部分”重叠。 (https://preshing.com/20120913/acquire-and-release-semantics/

你没有关键部分。

val
的加载可能发生在 CAS 之前,但这仍然没问题,因为那时您仍然会加载初始值。 并且没有
release
存储,因此其他线程不会知道您的
val
更新已完成。

我不会将其称为一次性自旋锁,这可能会产生误导性的影响,例如您将与

lock
变量同步。 (顺便说一句,
lock
需要下注
atomic_int
又名
_Atomic int
不简单
int
。)

© www.soinside.com 2019 - 2024. All rights reserved.