4

通常使用条件变量,以便它们引用的状态在互斥量下进行修改。但是,当状态只是一个单独的set-only标志时,不需要互斥体来阻止同时执行。所以你可能会做这样的事情:pthread_cond_signal或pthread_cond_broadcast调用是否意味着写入内存屏障?

flag = 1; 
pthread_cond_broadcast(&cvar); 

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

所以,我的问题是:pthread_cond_broadcastpthread_cond_signal调用意味着写入内存障碍?如果是这样,那么在相关的POSIX(或其他)规范中指定的位置在哪里? The spec在这一点上似乎不清楚。 (在Linux上,因为线程唤醒意味着一个完整的CPU内存屏障,并且跨库函数调用意味着编译器内存障碍),所以在实践中,这确实会导致内存障碍。不过,我对这里的规格保证感兴趣。

回答

7

不管它是否意味着内存屏障,代码仍然不正确。考虑读出侧:

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

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

+0

从技术上讲,POSIX确实承诺'pthread_cond_broadcast()'是内存屏障(http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_11)。但作为你的答案细节很好,这对于被问到的目的没什么用处。 –

3

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

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