pthread_mutex_lock和pthread_mutex_unlock函数是否调用内存栅栏/屏障指令?或者像 compare_and_swap 隐式这样的较低级别指令是否有内存障碍?
pthread_mutex_lock 和 pthread_mutex_unlock 函数是否调用内存栅栏/屏障指令?
它们确实如此,线程创建也是如此。
但是请注意,内存屏障有两种类型:编译器和硬件。
编译器屏障仅阻止编译器重新排序读写以及推测变量值,但不会阻止 CPU 重新排序。
硬件屏障阻止CPU重新排序读取和写入。满内存栅栏通常是最慢的指令,大多数时候您只需要具有获取和释放语义的操作(以实现自旋锁和互斥体)。
使用多线程,大多数时候你都需要这两个屏障。
任何其定义在此翻译单元中不可用(并且不是内在的)的函数都是编译器内存障碍。
pthread_mutex_lock
、pthread_mutex_unlock
、pthread_create
还发出硬件内存屏障,以防止 CPU 重新排序读取和写入。
来自 David R. Butenhof 的 POSIX 线程编程:
Pthreads 提供了一些有关内存可见性的基本规则。您可以信赖该标准的所有实现都遵循这些规则:
线程在调用
时可以看到的任何内存值也可以在新线程启动时被新线程看到。调用pthread_create
之后写入内存的任何数据可能不一定能被新线程看到,即使写入发生在线程启动之前。pthread_create
线程在解锁互斥锁时可以直接或通过等待条件变量看到的任何内存值,也可以被稍后锁定同一互斥锁的任何线程看到。同样,在互斥体解锁之后写入的数据不一定能被锁定互斥体的线程看到,即使写入发生在锁定之前。
无论线程终止时可以看到什么内存值,无论是通过取消、从其启动函数返回,还是通过调用
,通过调用pthread_exit
加入已终止线程的线程也都可以看到。当然,线程终止后写入的数据不一定能被加入的线程看到,即使写入发生在加入之前。pthread_join
线程在发出信号或广播条件变量时可以看到的任何内存值也可以被该信号或广播唤醒的任何线程看到。而且,再一次,在信号或广播之后写入的数据可能不一定能被唤醒的线程看到,即使写入发生在唤醒之前。
另请参阅 C++ and Beyond 2012:Herb Sutter -atomic<> Weapons 了解更多详细信息。
请查看 POSIX 规范的第 4.12 节。
应用程序应确保限制多个控制线程(线程或进程)对任何内存位置的访问,以便在另一个控制线程可以修改内存位置时,任何控制线程都无法读取或修改该内存位置。这种访问是通过使用同步线程执行以及相对于其他线程同步内存的函数来限制的。[强调我的]
然后给出了同步内存的函数列表,以及一些附加注释。
如果在某些架构上需要内存屏障指令,则必须使用这些指令。
关于
compare_and_swap
:这不在 POSIX 中;检查文档以了解您正在使用的任何内容。例如,IBM 为 AIX 5.3 定义了一个 compare_and_swap
函数。 它没有完整的内存屏障语义文档说明说:
如果将compare_and_swap用作锁定原语,请在任何关键部分的开头插入一个isync。
从这个文档中我们可以猜测 IBM 的
compare_and_swap
具有发布语义:因为文档不需要关键部分结尾的屏障。获取处理器需要发出 isync 以确保它没有读取过时的数据,但发布处理器不需要执行任何操作。
在指令级别,一些处理器具有某些同步保证的比较和交换,而有些则没有。