【问题标题】:UNIX Portable Atomic OperationsUNIX 可移植原子操作
【发布时间】:2010-11-10 22:18:20
【问题描述】:

在 C 中是否有一种 (POSIX-) 可移植方式用于原子变量操作,类似于使用 pthread 的可移植线程?

原子操作是像“增量和获取”这样以原子方式执行的操作,这意味着没有上下文切换可以干扰操作。在 Linux 内核空间中,我们必须输入 atomic_t,在 Java 中,我们有 java.util.concurrent.atomic 包。

在 Linux 上,atomic.h 文件提供原子操作,但包含依赖于平台,例如#include <asm-x86_64/atomic.h> 并且在 Mac OS X 上以类似的方式不可用。

【问题讨论】:

标签: c concurrency posix atomic


【解决方案1】:

从 C11 开始,有一个可选的 Atomic library 提供原子操作。这可以移植到任何具有 C11 编译器(如 gcc-4.9)的平台,该编译器具有此可选功能。

可以用__STDC_NO_ATOMICS__和<stdatomic.h>检查原子的存在

atomic.c

#include <stdio.h>
#include <stdlib.h>
#ifndef __STDC_NO_ATOMICS__
#include <stdatomic.h>
#endif

int main(int argc, char**argv) {
    _Atomic int a;
    atomic_init(&a, 42);
    atomic_store(&a, 5);
    int b = atomic_load(&a);
    printf("b = %i\n", b);

    return EXIT_SUCCESS;
}

编译器调用

clang -std=c11 atomic.c
gcc -std=c11 atomic.c

【讨论】:

    【解决方案2】:

    对于将来偶然发现这一点的任何人,C11 原子是现在最好的方法 - 我相信它们将包含在 GCC 4.9 中。

    【讨论】:

    【解决方案3】:

    既然您要求使用 OS X:

    (并且由于在此线程中提出了跨平台性。)

    OS X 有函数 OSAtomicAdd32() 和朋友。它们在“/usr/include/libkern/OSAtomic.h”中声明。 请参阅The Threading Programming guide,“使用原子操作”部分。

    对于 Windows,还有 InterlockedIncrement() 和朋友(参见 MSDN)。

    连同 gcc 内置 __sync_fetch_and_add() 和朋友(已在上面链接),你应该有 适用于每个主要桌面平台。

    请注意,我自己还没有使用它们,但可能会在接下来的几天内使用它们。

    【讨论】:

      【解决方案4】:

      不,POSIX 没有指定任何可移植的无锁/原子操作。这就是他们有 pthread 的原因。

      您要么不得不使用非标准方式,要么坚持使用 ptrheads 以实现可移植性。

      【讨论】:

        【解决方案5】:

        C11 atomics 最小可运行示例

        通过在 glibc 2.28 中添加线程,我们可以在纯 C11 中进行原子处理和线程处理。

        示例来自: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)
        {
            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
        

        由于线程间对非原子变量的快速访问,非原子计数器很可能小于原子计数器。

        可以在以下位置找到 pthreads 示例:How do I start threads in plain C?

        通过从源代码编译 glibc 在 Ubuntu 18.04 (glibc 2.27) 中进行测试:Multiple glibc libraries on a single host Ubuntu 18.10 具有 glibc 2.28,所以应该可以在那里工作。

        【讨论】:

        • 但如果我们坚持使用 glibc 2.27,stdatomic 是否可以使用 pthreads?
        • glibc 2.28 自带 Ubuntu 18.10 或 19.04
        【解决方案6】:

        AFAIK 没有跨平台的方法来执行原子操作。那里可能有一个图书馆,但我不知道。不过,自己动手并不是特别难。

        【讨论】:

          【解决方案7】:

          我认为没有。

          在许可证允许的情况下,解决它的一种方法是从例如复制相关的每个架构实现。 Linux 内核空间。我没有密切关注这些原语的演变,但我猜它们确实是原语,即不依赖于内核中的其他服务或 API。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2010-10-03
            • 1970-01-01
            • 1970-01-01
            • 2019-02-03
            • 2012-09-16
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多