简体   繁体   English

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

[英]How to use atomic variables in C?

I need to use an atomic variable in C as this variable is accessed across different threads.我需要在 C 中使用一个原子变量,因为这个变量是跨不同线程访问的。 Don't want a race condition.不想要竞争条件。

My code is running on CentOS.我的代码在 CentOS 上运行。 What are my options?我有哪些选择?

C11 atomic primitives C11 原子原语

http://en.cppreference.com/w/c/language/atomic 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

Added in glibc 2.28.在 glibc 2.28 中添加。 Tested in Ubuntu 18.04 (glibc 2.27) by compiling glibc from source: Multiple glibc libraries on a single host Later also tested on Ubuntu 20.04, glibc 2.31.通过从源代码编译 glibc 在 Ubuntu 18.04 (glibc 2.27) 中测试: 单个主机上的多个 glibc 库后来也在 Ubuntu 20.04、glibc 2.31 上进行了测试。

Example adapted from: https://en.cppreference.com/w/c/language/atomic示例改编自: https : //en.cppreference.com/w/c/language/atomic

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);
}

Compile and run:编译并运行:

gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c -pthread
./main.out

Possible output:可能的输出:

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

The non-atomic counter is very likely to be smaller than the atomic one due to racy access across threads to the non atomic variable.由于跨线程访问非原子变量,非原子计数器很可能小于原子计数器。

Disassembly analysis at: How do I start threads in plain C?反汇编分析: 如何在普通 C 中启动线程?

If you are using GCC on your CentOS platform, then you can use the __atomic built-in functions .如果您在 CentOS 平台上使用 GCC,那么您可以使用__atomic内置函数

Of particular interest might be this function:特别感兴趣的可能是这个函数:

— Built-in Function: bool __atomic_always_lock_free (size_t size, void *ptr) — 内置函数: bool __atomic_always_lock_free (size_t size, void *ptr)
This built-in function returns true if objects of size bytes always generate lock free atomic instructions for the target architecture.如果size字节的对象始终为目标架构生成无锁原子指令,则此内置函数返回 true。 size must resolve to a compile-time constant and the result also resolves to a compile-time constant. size必须解析为编译时常量,结果也解析为编译时常量。

ptr is an optional pointer to the object that may be used to determine alignment. ptr是指向对象的可选指针,可用于确定对齐。 A value of 0 indicates typical alignment should be used.0表示应使用典型对齐方式。 The compiler may also ignore this parameter.编译器也可能会忽略此参数。

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

I am going to toss in my two cents in case someone benefits.如果有人受益,我将投入我的两分钱。 Atomic operations are a major problem in Linux.原子操作是 Linux 中的一个主要问题。 I used gatomic.h at one time only to find it gone.我曾经使用 gatomic.h 却发现它不见了。 I see all kinds of different atomic options of either questionable reliability or availability -- and I see things changing all the time.我看到了各种不同的原子选项,无论是可靠性还是可用性都有问题——而且我看到事情一直在变化。 They can be complex with tests needed by O/S level, processor, whatever.它们可能会因 O/S 级别、处理器等所需的测试而变得复杂。 You can use a mutex -- not only complex by dreadfully slow.您可以使用互斥锁——不仅复杂而且非常慢。

Although perhaps not ideal in threads, this works great for atomic operations on shared memory variables.尽管在线程中可能并不理想,但这对于共享内存变量的原子操作非常有用。 It is simple and it works on every O/S and processor and configuration known to man (or woman), dead reliable, easy to code, and will always work.它很简单,适用于男性(或女性)已知的每个操作系统、处理器和配置,非常可靠,易于编码,并且将始终有效。

Any code can me made atomic with a simple primitive -- a semaphore.任何代码都可以通过一个简单的原语——信号量实现原子化。 It is something that is true/false, 1/0, yes/no, locked/unlocked -- binary.它是真/假、1/0、是/否、锁定/解锁——二进制。

Once you establish the semaphore:建立信号量后:

set semaphore   //must be atomic

do all the code you like which will be atomic as the semaphore will block for you做所有你喜欢的代码,这些代码将是原子的,因为信号量会为你阻塞

release semaphore  //must be atomic

Relatively straight forward except the "must be atomic" lines.除了“必须是原子”的行外,相对简单。

It turns out that you easily assign your semaphores a number (I use a define so they have a name like "#define OPEN_SEM 1" and "#define "CLASS_SEM 2" and so forth.事实证明,您可以轻松地为信号量分配一个数字(我使用了定义,因此它们的名称类似于“#define OPEN_SEM 1”和“#define“CLASS_SEM 2”等等。

Find out your largest number and when your program initializes open a file in some directory (I use one just for this purpose).找出您的最大数字,并在您的程序初始化时在某个目录中打开一个文件(我仅为此目的使用一个)。 If not there create it:如果没有创建它:

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
}

Now to gain a semaphore:现在获得一个信号量:

Now use your semaphore number to "lock" a "record" in your file of length one byte.现在使用您的信号量编号“锁定”长度为一字节的文件中的“记录”。 Note -- the file will never actually occupy disk space and no disk operation occurs.注意——该文件永远不会实际占用磁盘空间,也不会发生磁盘操作。

//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
}

To test if the semaphore is held:测试信号量是否被保持:

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

To release the semaphore:释放信号量:

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

And all the other things you can do with lockf -- blocking/non-blocking, etc.你可以用 lockf 做的所有其他事情——阻塞/非阻塞等。

Note -- this is WAY faster than a mutex, it goes away if the process dies (a good thing), simple to code, and I know of no operating system with any processor with any number of them or number of cores that cannot atomically lock a record ... so simple code that just works.注意——这比互斥锁快得多,如果进程死了它就会消失(一件好事),代码很简单,而且我知道没有任何操作系统具有任何数量的处理器或不能原子化的内核数量锁定一条记录...如此简单的代码就行了。 The file never really exists (no bytes but in directory), seems to be no practical limit to how many you may have.该文件从未真正存在(没有字节,但在目录中),似乎对您可能拥有的数量没有实际限制。 I have used this for years on machines with no easy atomic solutions.我多年来一直在没有简单原子解决方案的机器上使用它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM