0

对于仅在持有锁时访问的变量,我是否需要 volatile 限定符?n在此代码中,可以从可能更改concurrent_foo并发执行时的行为中删除 volatile 限定符。

#ifndef __GNUC__
#error __sync_lock builtins are only available with GCC
#endif

volatile int n = 0;
static volatile int lock = 0;

void concurrent_foo () {
    while (__sync_lock_test_and_set (&lock, 1));
    // Non-atomic operation, protected by spinlock above.
    int x = n % 2 + 1;
    n = n + x;
    __sync_lock_release (&lock);
}

我知道 volatile 限定符指示编译器不要优化对变量的内存访问。我也知道 __sync_lock 内置函数发出一个(完整的?)内存屏障,内存访问不应该跨越。但是,在此示例代码中,可以安全地获取n,将其缓存在寄存器中,计算新值,然后将其写回n.

使用 GCC 编译到 i686 源代码,-O3发现两次内存提取是不必要的:

concurrent_foo:
        movl        $1, %edx
.L2:
        movl        %edx, %eax
        xchgl        lock, %eax
        testl        %eax, %eax
        jne        .L2
        movl        n, %eax
        movl        n, %edx
        movl        %eax, %ecx
        shrl        $31, %ecx
        addl        %ecx, %eax
        andl        $1, %eax
        subl        %ecx, %eax
        leal        1(%edx,%eax), %eax
        movl        %eax, n
        movl        $0, lock
        ret

如果没有 volatile 限定符,我会得到微妙不同的代码,其中n仅获取一次:

concurrent_foo:
        movl        $1, %edx
.L2:
        movl        %edx, %eax
        xchgl        lock, %eax
        testl        %eax, %eax
        jne        .L2
        movl        n, %edx
        movl        %edx, %ecx
        shrl        $31, %ecx
        leal        (%edx,%ecx), %eax
        andl        $1, %eax
        subl        %ecx, %eax
        leal        1(%edx,%eax), %eax
        movl        %eax, n
        movl        $0, lock
        ret

在这两种情况下,内存访问n都会在持有锁时发生,因此应该是“正确的”。但是,我不确定我是否真的能保证这一点。volatile 限定符阻止了我想要的性能优化,并且不会影响操作的结果(在任何时候都不会n是偶数)。

4

0 回答 0