4

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

flag = 1;
pthread_cond_broadcast(&cvar);

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

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

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

4

3 回答 3

9

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

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

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

于 2011-08-30T12:22:20.193 回答
3

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

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

于 2011-08-30T11:19:06.223 回答
0

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

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

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

所以:不要这样做。

于 2020-08-15T22:25:34.870 回答