C中是否有(POSIX-)可移植方式用于原子变量操作,类似于使用pthread的便携式线程?
原子操作是像“递增和获取”这样的操作,它们以原子方式执行,这意味着没有上下文切换会干扰操作。在Linux内核空间中,我们必须atomic_t类型,在Java中我们有java.util.concurrent.atomic包。
在Linux上,atomic.h文件提供原子操作,但include依赖于平台,例如: #include <asm-x86_64/atomic.h>
并且它在Mac OS X上不能以类似的方式提供。
答案 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。