通常使用条件变量,以便它们引用的状态在互斥锁下被修改。但是,当状态只是一个仅设置标志时,不需要互斥锁来防止同时执行。所以有人可能想做这样的事情:
flag = 1;
pthread_cond_broadcast(&cvar);
但是,如果pthread_cond_broadcast
意味着写入内存屏障,则这是安全的。否则,等待线程可能会在标志写入之前看到条件变量广播。也就是说,等待线程可能会唤醒,消耗cvar信号,但看到标志仍为0
。
所以,我的问题是:pthread_cond_broadcast
和pthread_cond_signal
调用是否意味着写内存障碍?如果是,那么在相关的POSIX(或其他)规范中指定了哪里? The spec在这一点上似乎不清楚。
注意:我知道,实际上,这确实会导致内存障碍(在Linux上,因为线程唤醒意味着完整的CPU内存障碍,而跨库函数调用意味着编译器内存障碍)。但是,我对这些规范是什么感兴趣。
答案 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级别的偶数类型的这种修改可能适用于非/有限的优化编译,并且当允许编译器分析程序以推断出东西时,在更高的优化中失败。
所以:不要这样做。