如何在C语言中使用原子变量?
我需要在C中使用一个原子变量,因为这个变量是跨不同线程访问的。我不想要比赛条件如何在C语言中使用原子变量?,c,linux,atomic,C,Linux,Atomic,我需要在C中使用一个原子变量,因为这个变量是跨不同线程访问的。我不想要比赛条件 我的代码正在CentOS上运行。我的选项是什么?如果您在CentOS平台上使用GCC,则可以使用 特别令人感兴趣的可能是这一职能: -内置函数:bool\uu原子\u始终\u锁定\u自由(大小\u t大小,无效*ptr) 如果大小字节的对象总是为目标体系结构生成无锁原子指令,则此内置函数返回true大小必须解析为编译时常量,并且结果也解析为编译时常量。 ptr是指向可用于确定对齐的对象的可选指针。值0表示应使用典型对
我的代码正在CentOS上运行。我的选项是什么?如果您在CentOS平台上使用GCC,则可以使用 特别令人感兴趣的可能是这一职能: -内置函数:
bool\uu原子\u始终\u锁定\u自由(大小\u t大小,无效*ptr)
如果
大小
字节的对象总是为目标体系结构生成无锁原子指令,则此内置函数返回true<代码>大小必须解析为编译时常量,并且结果也解析为编译时常量。
ptr
是指向可用于确定对齐的对象的可选指针。值0
表示应使用典型对齐方式。编译器也可以忽略此参数
if (_atomic_always_lock_free (sizeof (long long), 0))
C11原子原语 在glibc 2.28中添加。通过从源代码编译glibc在Ubuntu 18.04(glibc 2.27)中进行了测试:后来也在Ubuntu 20.04、glibc 2.31上进行了测试 示例改编自: main.c
#include <stdio.h>
#include <threads.h>
#include <stdatomic.h>
atomic_int acnt;
int cnt;
int f(void* thr_data)
{
(void)thr_data;
for(int n = 0; n < 1000; ++n) {
++cnt;
++acnt;
// for this example, relaxed memory order is sufficient, e.g.
// atomic_fetch_add_explicit(&acnt, 1, memory_order_relaxed);
}
return 0;
}
int main(void)
{
thrd_t thr[10];
for(int n = 0; n < 10; ++n)
thrd_create(&thr[n], f, NULL);
for(int n = 0; n < 10; ++n)
thrd_join(thr[n], NULL);
printf("The atomic counter is %u\n", acnt);
printf("The non-atomic counter is %u\n", cnt);
}
可能的产出:
The atomic counter is 10000
The non-atomic counter is 8644
由于线程间对非原子变量的快速访问,非原子计数器很可能比原子计数器小
拆卸分析在:我将投入我的两分钱,以防有人受益。原子操作是Linux中的一个主要问题。我曾经用过gatomic.h,结果发现它不见了。我看到了各种不同的原子选项,无论是可靠性还是可用性都值得怀疑——我看到事情一直在变化。它们可能会因O/S级、处理器等所需的测试而变得复杂。您可以使用互斥锁——不仅是非常慢的复杂互斥锁 虽然在线程中可能并不理想,但这对于共享内存变量的原子操作非常有效。它很简单,可以在每个O/S和处理器上工作,并且男人(或女人)都知道它的配置,非常可靠,易于编码,并且可以一直工作 任何代码都可以用一个简单的原语——一个信号量来实现原子化。它是真/假、1/0、是/否、锁定/解锁——二进制 建立信号量后:
set semaphore //must be atomic
result = lockf(ablockfd, F_ULOCK, 1); //after same lseek
执行您喜欢的所有代码,这些代码将是原子的,因为信号量将为您阻塞
release semaphore //must be atomic
除“必须是原子的”行外,其他行相对简单
事实证明,您可以轻松地为信号量分配一个数字(我使用define,因此它们的名称类似于“#define OPEN_SEM 1”和“#define”CLASS_SEM 2”,等等)
找出您的最大数字,并在程序初始化时在某个目录中打开一个文件(我使用一个仅用于此目的)。如果没有,请创建它:
if (ablockfd < 0) { //ablockfd is static in case you want to
//call it over and over
char *get_sy_path();
char lockname[100];
strcpy(lockname, get_sy_path());
strcat(lockname, "/metlock");
ablockfd = open(lockname, O_RDWR);
//error code if ablockfd bad
}
要测试信号量是否保持不变,请执行以下操作:
result = lockf(ablockfd, F_TEST, 1); //after same lseek
要释放信号量,请执行以下操作:
set semaphore //must be atomic
result = lockf(ablockfd, F_ULOCK, 1); //after same lseek
还有你可以用lockf做的所有其他事情——阻塞/非阻塞,等等
注意——这比互斥锁快得多,如果进程死掉它就会消失(这是件好事),代码简单,而且我知道没有任何操作系统的处理器有任意数量的互斥锁,或者内核数量不能自动锁定记录……所以简单的代码可以正常工作。文件永远不存在(没有字节,但在目录中),似乎对您可能拥有的数量没有实际限制。我已经在没有简单原子解决方案的机器上使用了多年。此外,这可能值得一读:看起来CentOS的pthreads是可用的,因此您可以使用互斥来同步访问。类型
volatile sig_atomic_t
保证具有原子访问ss@RickyMutschlechner:volatile
不能做到这一点。请参考你链接到的维基百科文章:“对volatile变量的操作不是原子的……”RickyMutschlechner:你不能仅仅使用volatile
实现自旋锁,因为即使编译器没有,处理器也会重新排序IO。问题还说“不想要竞争条件”,如果您担心竞争条件,那么volatile
就不是合适的工作工具。
result = lockf(ablockfd, F_ULOCK, 1); //after same lseek