_C 中任意大小的原子结构体赋值?

问题描述 投票:0回答:1

C 标准规定*任何类型(明确禁止的类型除外 - 见下文)都可以用

_Atomic
限定,例如:

struct S {
    char a[4096];
};

_Atomic struct S s1, s2;

这是否意味着这样的分配

struct
:

s1 = s2;          // atomic?

是原子的吗? 生成的 x86-64 程序集(例如)调用了

__atomic_load
__atomic_store
,这是否意味着复制整个 4K 数组是原子完成的,即,没有其他线程可以访问
s2
,而加载它并且在存储到其中时没有其他线程可以访问
s1

如果它不是真正的原子性,为什么编译器至少不发出警告? 在这样的

_Atomic
上允许
struct
有什么用?

我的印象是你只能制作小尺寸的物体(

sizeof(T)
<= 16) be atomic.


* C11 标准,§6.7.2.4¶3:

原子类型说明符中的类型名称不得引用数组类型、函数类型、原子类型或限定类型。

同上。 §6.7.3¶3:

_Atomic
限定符修饰的类型不能是数组类型或函数类型。

对我来说,这意味着任何未明确禁止的类型都是允许的。 FWIW,

gcc
clang
都毫无怨言地编译了代码。

如果您对

struct
内的数组感到困扰,那么您可以按照Peter的建议进行操作,例如:

struct S2 {
    int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10;
    int a11, a12, a13, a14, a15, a16, a17, a18, a19, a20;
    // ...
};

为这样的

struct
分配两个变量的代码也将毫无抱怨地编译并生成涉及
__atomic_load
__atomic_store
的机器代码。

c language-lawyer atomic
1个回答
0
投票

这是否意味着这样一个结构的赋值:

s1 = s2;          // atomic?

是原子的吗?

这似乎是语言规范的意图,就其本身的“原子”意义而言,尽管它并没有完全按照这些术语进行阐述。 但是,您需要了解规范中“原子”的含义与 C 内存模型相关,而不一定与实现使用的 CPU 指令类型相关。

C 定义所有格式正确的

_Atomic
限定(或 C23 中的
atomic
限定)类型都是“原子类型”(C23 6.2.5/25),并且它指定了对象行为的各种细节具有原子类型,无需进一步区分。 特别是,它说

具有原子类型的对象的加载和存储是通过

memory_order_seq_cst
语义完成的。

(C23 6.2.6.1/9)

我采用内存排序规范来假定原子性。

此外,虽然简单分配的规范没有具体说明此类分配是“原子地”执行的,但它们确实引用了 6.2.6.1/9(在 C23 6.5.17.1/2 中)。 此外,复合赋值操作的规范规定,当左操作数具有原子类型时

当[左操作数]具有原子类型时,复合赋值是具有

memory_order_seq_cst
内存顺序语义的读取-修改-写入操作。

这不仅是对内存排序语义的另一个引用,而且“读取-修改-写入”是原子操作的术语,特别是在具有原子类型的左操作数的上下文中给出。

我的印象是你只能制作小尺寸的物体(sizeof(T)<= 16) be atomic.

语言规范没有这样的限制。 正如您所观察到的,它确实规定了数组类型和函数类型不得具有

_Atomic
限定,但除此之外,语言语法允许任何类型具有
_Atomic
限定,定义了这样的含义类型,并指定与原子类型相关的行为,而不将此类类型划分为不同的类别(除非下面描述)。

我认为您的印象与对如何实现原子类型和操作的特定期望有关,但规范并未解决该级别。 但请注意,C 明确允许在锁的帮助下(透明地)完成原子操作。 为此,它允许原子类型的大小可以不同于其非原子类型的大小,并且它提供了函数

atomic_is_lock_free()
来确定特定原子类型是否是无锁的。 您应该期望实现将使用此功能来实现 C 原子操作语义,而它们不能或不会选择通过专用 CPU 指令或类似的低级机制来实现。

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