【问题标题】:Does a pthread_cond_signal or pthread_cond_broadcast call imply a write memory barrier?pthread_cond_signal 或 pthread_cond_broadcast 调用是否意味着写入内存屏障?
【发布时间】:2011-08-29 19:29:10
【问题描述】:

通常使用条件变量,以便在互斥锁下修改它们所引用的状态。但是,当状态只是一个仅设置标志时,不需要互斥锁来防止同时执行。所以有人可能想做这样的事情:

flag = 1;
pthread_cond_broadcast(&cvar);

然而,这只有在pthread_cond_broadcast 暗示写内存屏障时才是安全的;否则,等待线程可能会在标志写入之前看到条件变量广播。也就是等待线程可能会被唤醒,消费cvar信号,但是看到flag还是0

所以,我的问题是:pthread_cond_broadcastpthread_cond_signal 调用是否暗示写入内存屏障?如果是这样,这在相关的 POSIX(或其他)规范中在哪里指定? The spec 在这一点上似乎不清楚。

注意:我知道,在实践中,这确实会导致内存屏障(在 Linux 上,因为线程唤醒意味着完整的 CPU 内存屏障,而跨库函数调用意味着编译器内存屏障)。但是,我对规范保证的内容感兴趣。

【问题讨论】:

    标签: c++ multithreading pthreads language-lawyer memory-barriers


    【解决方案1】:

    无论是否暗示内存屏障,代码仍然不正确。考虑读取方面:

    while (flag == 0)
        pthread_cond_wait(&cvar, &mutex);
    

    如果读取端在测试flag == 0 和执行等待之间暂停,则写入端可以执行flag = 1; pthread_cond_signal(&cvar);。然后,读取端将完全错过唤醒 - 它将永远等待。请记住,唤醒没有排队 - 如果在发出条件变量信号时没有服务员,则信号无效。为了避免这种情况,写端无论如何都需要锁定互斥体。

    【讨论】:

    【解决方案2】:

    在 POSIX 下,如果您从一个线程写入变量并从另一个线程读取它,那么您必须使用互斥锁来保护它。 pthread_cond_broadcast 也不例外。

    如果您的平台/编译器提供原子变量,那么他们可能会对这些变量做出额外的保证。例如,如果 flag 是 C++11 std::atomic<int>,那么这段代码是可以的。

    【讨论】:

      【解决方案3】:

      编译器有权假设非易失性对象的值没有被虚假更改。它本质上是能够假设即使是最简单的 CSE 优化也是有效的(并且它使优化无法检测到)。

      它是一个基本不变量,是任何关于可变状态的局部推理的基础。

      这种对在 CPU 级别具有原子加载和存储的偶数类型的修改可能适用于非/有限优化编译,并且当允许编译器分析程序以推断内容时,在更高优化时会失败。

      所以:不要这样做。

      【讨论】:

        猜你喜欢
        • 2014-12-07
        • 2013-07-18
        • 2018-07-12
        • 2014-08-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-30
        相关资源
        最近更新 更多