【问题标题】:Memory order introduced by atomic_exchange_explicit/atomic_exchangeatomic_exchange_explicit/atomic_exchange 引入的内存顺序
【发布时间】:2019-09-07 10:55:33
【问题描述】:

我试图理解atomic_exchange[_explicit] 引入的内存排序并编写了以下代码:

static _Atomic int i = 123;

void *update(void *ignored){
    if(atomic_exchange_explicit(&i, 200, memory_order_release) != 200){ //1
        printf("Updated\n", stdout);
    } 
}

int main(int args, const char *argv[]){
    pthread_t t1;
    pthread_create(&t1, NULL, &update, NULL);

    pthread_t t2;
    pthread_create(&t2, NULL, &update, NULL);

    sleep(1000);
}

问题Updated\n 是否可能会被打印两次(由两个线程)?

我认为行为未定义。即使我们在//1memory_order_release 替换为memory_order_acquire,也会保留UB。 acq/rel/acq_rel 生成的代码相同:https://godbolt.org/z/sFjcve

这是因为我们有数据竞争,因为要保持与关系的同步,我们需要一个操作是 acquire 操作,下一个操作是 release 操作,并且释放操作读取由副作用写入的值从获取操作的释放序列5.1.2.4(p11)

特别是执行释放操作的原子操作A 在对象 M 上与执行的原子操作 B 同步 对 M 的获取操作并读取由任何副作用写入的值 在以A为首的发布序列中。

因此,明确定义此代码行为的唯一方法是将atomic_exchange_explicit(&i, 200, memory_order_release) != 200 替换为atomic_exchange(&i, 200) != 200

【问题讨论】:

    标签: c multithreading concurrency stdatomic


    【解决方案1】:

    你错了。无论请求的内存顺序如何,交换都是原子的。内存排序说明了这种读取、写入或读取-修改-写入操作如何与其他操作交互,但该操作始终是原子的。

    【讨论】:

    • 我没有注意到7.17.3(p12)原子读取-修改-写入操作应始终读取与读取-修改相关的写入之前存储的最后一个值(按修改顺序)写操作。.所以看来我甚至可以指定 memory_order_relaxed 并且它不会导致数据竞争,对吗?
    • 正确。内存排序指定此原子操作如何与其他操作交互,正如各种内存排序在其描述性文本中所解释的那样。
    猜你喜欢
    • 2016-08-27
    • 2015-10-24
    • 2021-11-02
    • 2021-08-27
    • 2014-03-24
    • 2017-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多