代码之家  ›  专栏  ›  技术社区  ›  Paul J. Lucas

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

  •  1
  • Paul J. Lucas  · 技术社区  · 1 周前

    C标准说* 任何 类型(明确禁止的除外,见下文)可以用 _Atomic 例如:

    struct S {
        char a[4096];
    };
    
    _Atomic struct S s1, s2;
    

    这是否意味着这样的任务 struct :

    s1 = s2;          // atomic?
    

    是原子?这个 generated x86-64 assembly (例如)呼叫 __atomic_load __atomic_store ,这是否意味着复制整个4K阵列是原子性的,即没有其他线程可以访问 s2 加载时,其他线程无法访问 s1 在存储过程中?

    如果它不是真正的原子,为什么编译器至少不发出警告?允许有什么用 _原子 在这样一个 结构 ?

    我以为你只能做小东西( sizeof(T) <=16) 原子。


    *C11标准,§6.7.2.43:

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

    同上,第6.7.33节:

    由修改的类型 _原子 限定符不能是数组类型或函数类型。

    对我来说,这意味着任何没有明确禁止的类型都是允许的。FWIW,两者 gcc clang 毫无怨言地编译代码。

    如果你被里面的数组所困扰 结构 ,那么你可以按照彼得的建议去做,例如:

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

    为这样的两个变量赋值的代码 结构 还将毫无怨言地编译并生成涉及以下内容的机器代码 __原子负载 __原子存储 .

    2 回复  |  直到 1 周前
        1
  •  6
  •   John Bollinger    1 周前

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

    s1 = s2;          // atomic?
    

    是原子?

    这似乎是语言规范的意图,在它自己的“原子”意义上,尽管它并没有完全用这些术语来描述。然而,您需要了解,规范的“原子”意义与C内存模型有关,而不一定与实现使用的CPU指令类型有关。

    C定义所有格式良好 _Atomic -合格(或 atomic -在C23中合格)类型是“原子类型”(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中)。此外,复合分配操作的规范说

    当[左操作数]具有原子类型时,复合赋值是一种读-修改-写操作 存储顺序 记忆顺序语义。

    (C23 6.5.17.3/4)

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

    我的印象是,你只能制作小尺寸的物体(sizeof(T)<=16) 原子。

    语言规范没有这样的限制。正如你所观察到的,它 设置一个约束,即数组类型和函数类型不得 _原子 -限定,但除此之外,语言语法允许任何对象类型 _原子 -合格后,规范定义了这种类型的含义,并指定了与原子类型相关的行为,而没有将这些类型划分为不同的类别(除非下面描述)。

    我想你的印象与对如何实现原子类型和操作的特定期望有关,但规范没有解决这个问题。然而,请注意,C明确允许在锁的帮助下(透明地)完成原子操作。为此,它允许原子类型的大小可能不同于其非原子对应物的大小,并提供功能 atomic_is_lock_free() (C23 7.17.5.2),用于确定特定原子类型是否无锁。您应该期望实现将使用锁来实现C原子操作语义,无论出于什么原因,它们都不会通过专用CPU指令或类似的低级机制来实现。

        2
  •  0
  •   supercat    1 周前

    该标准假设程序不需要任何与原子或内存屏障相关的东西,除非它们的目标实现可以在需要时使用锁对任何大小的对象执行任何类型的原子操作,忽略了这样一个事实 有用的 支持这种语义可能需要了解编译器编写者不可能知道的目标环境,在某些目标上可能根本不可能。

    GCC通过要求程序员提供函数来对任意大小的存储区域执行“原子”读取、写入和其他操作,从而解决了这个问题。如果程序员提供了按需运行的函数,原子操作将按指定执行。如果程序员不提供这样的函数,那么无论标准怎么说,实现都无法支持这样的语义。当针对标准假设不成立的平台时,这种处理方式可以说违背了标准的文字(标准的文字要求此类实现声称根本不支持原子操作),但支持确实存在的操作通常比放弃对原子的所有支持更有用。