pthread_cond_signal或pthread_cond_broadcast调用是否意味着写入内存屏障?

时间:2011-08-29 19:29:11

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

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

flag = 1;
pthread_cond_broadcast(&cvar);

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

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

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

3 个答案:

答案 0 :(得分:7)

无论是否意味着内存障碍,代码仍然不正确。考虑一下阅读方:

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

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

答案 1 :(得分:3)

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

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

答案 2 :(得分:0)

编译器有权假定非易失性对象的值不会被虚假地更改。基本上可以假设,即使最简单的CSE优化都有效(并且使得优化无法检测到)。

这是基本不变性,是任何关于可变状态的局部推理的基础。

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

所以:不要这样做。