如何一次自动地,安全地和线程安全地初始化全局变量

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

我创建了一个全局自旋锁变量和一个检查变量:

pthread_spinlock_t locking;
int check = 1;

现在我想一次初始化此变量:

if (check == 1)
  {
    // atomic part begins here
    pthread_spin_init(&locking, PTHREAD_PROCESS_SHARED);
    check = 0;
    // atomic part ends here
  }

所以这两行应该以原子方式发生,并且我想使此线程保存起来。

有人知道如何处理吗?

我无法使用互斥锁锁定整个if语句。是否可以通过汇编指令使其原子化?

c multithreading assembly thread-safety atomic
1个回答
1
投票

因此您可能正在修改if(check == 1)时其他线程可以正在运行check?无法通过if内的锁来使其完全安全,因为您具有对关键部分的读取访问outside。那将是数据争用UB。

如果if(check==1)部分应该一直运行,并且几乎总是发现它为假,那么您希望使该检查非常便宜并且可扩展到多个并行读取器。用锁保护该访问效率不高;所有读者都必须修改锁本身。

C11引入了<stdatomic.h>,可让您方便地访问原子负载,存储和RMW。您可以将check转换为atomic_int

然后对它的只读访问与不带锁定的普通全局访问便宜一样。如果它经常被读取并且没有被写入,它可以在每个内核的私有L1d缓存中保持高温。

#include <stdatomic.h>
#include <stdbool.h>

atomic_int check = 1;

void foo() {
    int old = 1;
    if (atomic_load_explicit(&check, memory_order_relaxed) == old) {
        bool success = atomic_compare_exchange_strong(&check, &old, 0);
        if (success) {
            // this thread did the exchange
        }
        // else some other thread saw check=1 and beat us to the punch
        // and old is updated to the previous value of check
    }
}

Godbolt编译器浏览器上编译为check != 1快速路径的有效asm:

# gcc9.2 -O3 for x86-64
foo:
        mov     eax, DWORD PTR check[rip]     # plain asm load, atomic because it's aligned
        cmp     eax, 1
        je      .L4
        ret
.L4:
        xor     edx, edx
        lock cmpxchg    DWORD PTR check[rip], edx
        ret
check:
        .long   1

并且即使在像AArch64这样的顺序较弱的ISA上,价格也很便宜。

无法优化atomic_int的读取或将其吊离循环。

int tmp = check;类似于atomic_load_explicit,默认为memory_order_seq_cst。在x86上,这在asm上不会花费任何额外的费用,但是在其他ISA上,它需要障碍来进行负载排序。我用了relaxed;如果您希望它表示可以安全地读取其他数据,则应使用acquire或默认的seq_cst。


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