如何在C中使用原子变量?

问题描述 投票:2回答:3

我需要在C中使用原子变量,因为这个变量可以跨不同的线程访问。不想要比赛条件。

我的代码在CentOS上运行。我有什么选择?

c linux atomic
3个回答
3
投票

如果您在CentOS平台上使用GCC,那么您可以使用__atomic built-in functions

特别感兴趣的可能是这个功能:

- 内置功能:bool __atomic_always_lock_free (size_t size, void *ptr) 如果size字节的对象总是为目标体系结构生成无锁原子指令,则此内置函数返回true。 size必须解析为编译时常量,结果也会解析为编译时常量。

ptr是指向可用于确定对齐的对象的可选指针。值0表示应使用典型的对齐方式。编译器也可以忽略此参数。

      if (_atomic_always_lock_free (sizeof (long long), 0))

4
投票

C11原子基元

http://en.cppreference.com/w/c/language/atomic

_Atomic const int * p1;  // p is a pointer to an atomic const int
const atomic_int * p2;   // same
const _Atomic(int) * p3; // same

在glibc 2.28中添加。通过从源代码编译glibc在Ubuntu 18.04(glibc 2.27)中测试:Multiple glibc libraries on a single host

例如:https://en.cppreference.com/w/c/language/atomic

#include <stdio.h>
#include <threads.h>
#include <stdatomic.h>

atomic_int acnt;
int cnt;

int f(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);
}

编译并运行:

gcc -std=c11 main.c -pthread
./a.out

可能的输出:

The atomic counter is 10000
The non-atomic counter is 8644

非原子计数器很可能小于原子计数器,因为跨线程对非原子变量进行了严格的访问。

TODO:拆解并查看++acnt;编译的内容。


1
投票

如果有人受益,我将折腾我的两美分。原子操作是Linux中的一个主要问题。我一次只使用gatomic.h才发现它消失了。我看到了可疑的可靠性或可用性的各种不同的原子选项 - 我发现事情总是在变化。它们可能很复杂,需要O / S级别,处理器等所需的测试。你可以使用互斥体 - 不仅复杂,速度慢。

尽管在线程中可能并不理想,但这对于共享内存变量的原子操作非常有用。它很简单,适用于人(或女人)已知的每个操作系统和处理器和配置,可靠,易于编码,并且始终有效。

任何代码都可以用一个简单的原语 - 一个信号量 - 来创建原子。它是真/假,1/0,是/否,锁定/解锁 - 二进制。

一旦建立了信号量:

set semaphore   //must be atomic

做你喜欢的所有代码,因为信号量会阻塞你

release semaphore  //must be atomic

比较直接的除了“必须是原子”线。

事实证明,您可以轻松地为您的信号量分配一个数字(我使用一个定义,因此它们的名称类似于“#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
}

现在获得一个信号量:

现在使用您的信号量号码“锁定”长度为一个字节的文件中的“记录”。注意 - 该文件实际上永远不会占用磁盘空间,也不会发生磁盘操作。

//sem_id is passed in and is set from OPEN_SEM or CLASS_SEM or whatever you call your semaphores.

lseek(ablockfd, sem_id, SEEK_SET); //seeks to the bytes in file of 
                                   //your semaphore number
result = lockf(ablockfd, F_LOCK, 1);   

if (result != -1) {                    
   //got the semaphore
} else {
    //failed
}

要测试是否持有信号量:

result = lockf(ablockfd, F_TEST, 1);  //after same lseek

要释放信号量:

result = lockf(ablockfd, F_ULOCK, 1);   //after same lseek

还有你可以用lockf做的所有其他事情 - 阻塞/非阻塞等。

注意 - 这比互斥锁更快,如果进程终止(一件好事)就会消失,代码简单,我知道没有任何操作系统,任何处理器都有任意数量或核心数量不能自动化锁定记录...这么简单的代码才能正常工作。该文件永远不存在(没有字节,但在目录中),似乎没有实际限制你可能有多少。我已经在没有简单原子解决方案的机器上使用了多年。

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