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

问题描述 投票:0回答:4

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

flag = 1;
pthread_cond_broadcast(&cvar);

但是,只有当

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

所以,我的问题是:

pthread_cond_broadcast
pthread_cond_signal
调用是否意味着写内存屏障?如果是这样,相关 POSIX(或其他)规范中在哪里指定了这一点? 规范在这一点上似乎不清楚。

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

c++ multithreading pthreads language-lawyer memory-barriers
4个回答
9
投票

无论是否存在内存障碍,代码仍然不正确。考虑阅读方面:

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

如果读取端在测试

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


3
投票

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

pthread_cond_broadcast
也不例外。

如果您的平台/编译器提供原子变量,那么他们可能会对这些变量做出额外的保证。例如,如果

flag
是 C++11
std::atomic<int>
,那么此代码就可以。


0
投票

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

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

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

所以:不要这样做。


0
投票

据我所知,

pthread_cond_signal()
pthread_cond_broadcast
不需要内存屏障,因为
pthread_cond_wait()
是在互斥锁锁定的情况下调用的,编写者在更改变量时还需要锁定互斥体,当编写者释放锁时,有记忆障碍。

pthread_mutex_lock(&lock);
flag = 1;
pthread_mutex_unlock(&lock);    // memory barrier here
pthread_cond_broadcast(&cvar);

pthread_cond_broadcast()
之前打电话
pthread_mutex_unlock()
也对

© www.soinside.com 2019 - 2024. All rights reserved.