UNIX便携式原子操作

时间:2009-07-15 07:59:29

标签: c concurrency posix atomic

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

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

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

7 个答案:

答案 0 :(得分:13)

对于任何在未来偶然发现这种情况的人来说,C11原子是现在最好的方法 - 我相信它们将被纳入GCC 4.9。

答案 1 :(得分:12)

从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 :(得分:10)

因为您要求OS X:

(因为在这个帖子中提出了跨平台性。)

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

对于Windows,有 InterlockedIncrement()和朋友(请参阅MSDN)。

与gcc builtins __ sync_fetch_and_add()和朋友(已在上面链接)一起,你应该有 适用于每个主要桌面平台。

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

答案 3 :(得分:4)

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

你要么必须使用非标准方式,要么坚持使用ptrheads来实现可移植性。

答案 4 :(得分:3)

C11原子最小可运行示例

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

示例来自:https://en.cppreference.com/w/c/language/atomic

main.c

$servers = @("department1", "department2", "department3", "department4", "department5", "department6", "department7")
$serverNames = @("server1", "server2", "server3", "server4", "server5", "server6", "server7")
$reportDate = (Get-Date -UFormat %b) + "01" + (Get-Date -UFormat %Y)

foreach ($serverName in $serverNames) {

    $url = "https://ourwebsite.com/schedule-reports/reportname/" + $reportDate + "/12-33-0-PM/" + $serverName + "/monthlyreport.pdf"
    $request = Invoke-WebRequest $url -SessionVariable fb
    $form = $request.Forms[0]
    $form.Fields["username"] = "username"
    $form.Fields["password"] = "password"

    Invoke-WebRequest -Uri ($url + $form.Action) -WebSession $fb -Method POST -Body $form.Fields

    foreach ($server in $servers) {

        $downloadPath = "C:\Users\user\Documents\Reports_January_2019\File Servers\" + $server + "\"
        Invoke-WebRequest -Uri $url -OutFile ($downloadPath + "monthlyreport_" + $serverName + ".pdf")
    }
}

编译并运行:

#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

由于跨线程访问非原子变量,非原子计数器很可能小于原子计数器。

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

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

答案 5 :(得分:0)

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

答案 6 :(得分:0)

我认为没有。

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