比较和交换:通过不同的数据大小进行同步

时间:2016-04-15 23:58:40

标签: c++ gcc atomic

使用GCC内置C原子基元,我们可以使用__atomic_compare_exchange执行原子CAS操作。

与C ++ 11的std::atomic类型不同,GCC C原子基元在常规非原子积分类型上运行,包括支持cmpxchg16b的平台上的128位整数。 (C ++标准的未来版本可能支持与std::atomic_view类模板类似的功能。)

这让我有疑问:

如果对较大数据大小的原子CAS操作观察到同一内存位置上的原子操作发生的更改,但使用较小的数据大小会发生什么?

例如,假设我们有:

struct uint128_type {
  uint64_t x;
  uint64_t y;
} __attribute__ ((aligned (16)));

假设我们有一个uint128_type类型的共享变量,例如:

uint128_type Foo;

现在,假设线程A执行:

    Foo expected = { 0, 0 };
    Foo desired = { 100, 100 };
    int result = __atomic_compare_exchange(
        &Foo, 
        &expected, 
        &desired, 
        0, 
        __ATOMIC_SEQ_CST
    );

线程B确实:

    uint64_t expected = 0;
    uint64_t desired = 500;
    int result = __atomic_compare_exchange(
        &Foo.x, 
        &expected, 
        &desired, 
        0, 
        __ATOMIC_SEQ_CST
    );

如果线程A的16字节CAS发生在线程B的8字节CAS之前(或反之亦然),会发生什么? CAS是否正常失败?这甚至是定义的行为吗?这有可能"做正确的事情"在像x86_64这样支持16b CAS的典型架构上?

编辑:要清楚,因为它似乎引起了混淆,我询问上述行为是否由C ++标准定义。显然,所有__atomic_ *函数都是GCC扩展。 (但是,如果std::atomic_view变得标准化,未来的C ++标准可能必须定义这种东西。)我更普遍地询问典型现代硬件上原子操作的语义。例如,如果x86_64代码有2个线程对同一个内存地址执行原子操作,但是一个线程使用CMPXCHG8b而另一个线程使用CMPXCHG16b,那么一个线程对单个字执行原子CAS,而另一个是双字的原子CAS,这些操作的语义是如何定义的?更具体地说,CMPXCHG16b是否会失败,因为它会观察到由于之前的CMPXCHG8b而数据已从预期值发生变异?

换句话说,使用两种不同数据大小(但相同的起始内存地址)的两种不同CAS操作可以安全地用于线程之间的同步吗?

4 个答案:

答案 0 :(得分:4)

首先发生一个或另一个,每个操作都按照自己的语义进行。

在x86 CPU上,两个操作都需要锁定整个操作中保持的同一缓存行。因此,无论哪一个获得该锁定,都不会看到第二个操作的任何影响,无论哪个获得该锁定,都会看到第一个操作的所有效果。两个操作的语义都将得到充分尊重。

其他硬件可能会以其他方式实现此结果,但如果它没有达到此结果,除非它指定它有限制,否则它会被破坏。

答案 1 :(得分:3)

最终,原子数据将位于内存中的某个位置,并且所有对它的访问(或者当操作是原子的时,对各个缓存的访问)都将被序列化。由于CAS操作应该是原子的,它将作为一个整体执行或根本不执行。

话虽这么说,其中一项行动将取得成功,第二项行动将失败。订单是不确定的。

来自x86 Instruction Set Reference

  

该指令可与LOCK前缀一起使用,以允许指令以原子方式执行。为了简化处理器总线的接口,目标操作数接收写周期而不考虑比较结果。如果比较失败,则写回目标操作数;否则,源操作数将写入目标。 (处理器永远不会产生锁定读取,也不会产生锁定写入。)

显然,两个线程都会在锁定读取后尝试锁定写入(当与LOCK前缀一起使用时),这意味着只有其中一个将成功执行CAS,另一个将读取已经更改的值。 / p>

答案 2 :(得分:1)

原子的定义不太可能改变,强调我的

  

在并发编程中,一个操作(或一组操作)是原子的,   如果它可以线性化,不可分割或不可中断   系统的其余部分即时发生。原子性是一种   保证隔离来自并发进程。

你的问题是......

  

如果观察到更大数据大小的原子CAS操作会发生什么   由同一记忆中的原子操作发生的变化   位置,但使用较小的数据?

根据定义,使用原子操作修改的两个重叠的内存区域不能同时发生变异,即两个操作必须线性发生或不发生原子。

答案 3 :(得分:1)

在检查潜在冲突的原子操作之间的冲突时,硬件通常非常保守。甚至可能发生两个CAS操作到两个完全不同的,非重叠的地址范围可能被检测到彼此冲突。