原子结构的目的是什么?

4
根据C11 § 6.5.2.3规定:
Accessing a member of an atomic structure or union object results
in undefined behavior.

这是有道理的,因为一般情况下不能访问整个结构。但为什么_Atomic也是类型限定符而不仅仅是类型说明符呢?
换句话说,将结构体标记为_Atomic有什么目的?我既不能读取也不能写入其中的任何元素。
#include <stdatomic.h>

struct {
    int x;
} _Atomic foo;

int main(void) {
    foo.x = 42;     // write error
    return foo.x;   // read error
}

在GCC/Clang中,foo.x的两个访问都会导致警告/错误——这在C11标准中是完全没问题的。那么我为什么还要将结构体定义为_Atomic限定符呢?

2个回答

2
您不能访问单个成员,而只能访问整个结构。我认为,访问被视为原子单位的部分并没有太多意义。
一个典型的原子结构用例是结合两个指针,例如列表的头和尾。要操作这样的结构,您必须将当前值从原子复制到临时变量中进行修改,然后再复制回去。通过这种方式,您始终可以保证存储的值在任何时间都是一致的。
默认情况下,在整个原子结构上进行的所有此类操作都具有顺序一致性。

有趣,我没想到会有这样的行为。如果我可以原子地访问整个结构,那么我也会期望可以原子地访问单个元素。对我来说,如果我想修改单个元素,那么必须复制整个结构两次似乎是一种开销。但我可以想象这样的行为在许多架构上更容易指定和保证相同的行为。谢谢你的回答! - Max Maier

0

一些实现可能能够保证符合某些标准的结构具有原子行为。在C11标准发布之前,我不知道是否存在任何这样做的实现,但这似乎是很有可能的。如果一个实现有用地支持这样的结构,那么要求标准将它们视为约束违规肯定会令人恼火。

如果标准强制或至少建议实现将无法提供适当语义的原子结构定义尝试视为约束违规,那么将会很有帮助。但出于某种原因,标准的作者似乎非常不愿意承认一类程序,即一些实现将拒绝但在其他实现上具有定义行为的程序。一个好的标准应该尽量使有用的程序最大化,但标准却倾向于将一些实现支持而其他实现不支持的构造视为未定义行为,让实现自行决定是否支持它们,但没有提供任何安全的方法来确定它们是否被支持。


标准保证了所有原子限定的对象都是一致的。 - Jens Gustedt
@JensGustedt:如果程序调用未定义行为,一致性的保证就毫无意义。 - supercat
是的,那又怎样?你对C委员会的抱怨前提是可能存在“原子结构,其实现无法提供适当的语义”。这个前提是完全错误的。所有struct类型都可以被标记为原子,并且标准为它们提供了适当的语义。因此,C标准的任何实现都不可能处于这种情况。 - Jens Gustedt
@JensGustedt:也许我应该说“为成员访问提供适当的语义”[我认为这是结构语义的一部分]? - supercat
也许,但我甚至更不理解那意味着什么。原子操作的一个属性是不可分割性,一般要求原子struct访问单个成员是违反这一属性的。没有人应该期望在任何平台上这样的操作是“容易”的。通常对于struct,这将是一系列的加载、修改副本和循环中的比较-交换。 - Jens Gustedt
1
@JensGustedt:原子操作的重要之处在于对它们的操作是不可分割的。如果一个结构体有一个“启用”字段和一些数据,有时可能需要原子地设置“启用”字段并加载数据,但其他时候可能希望能够仅清除启用字段而不必对其他数据进行任何操作。如果“启用”通常为false,则代码可能会受益于读取启用字段,然后仅在它为true时读取整个结构体(认识到如果“启用”在两次读取之间变为false,则读取的努力将被浪费)。 - supercat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接