我创建了一个全局自旋锁变量和一个检查变量:
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语句。是否可以通过汇编指令使其原子化?
因此您可能正在修改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。