【发布时间】:2014-07-31 00:53:44
【问题描述】:
pthread_mutex_lock 和 pthread_mutex_unlock 函数是否调用内存栅栏/屏障指令?或者像 compare_and_swap 这样的低级指令是否隐含了内存屏障?
【问题讨论】:
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,也可以通过调用 @987654328 与终止线程连接的线程看到@。当然,线程终止后写入的数据不一定会被加入的线程看到,即使写入发生在加入之前。线程在发出信号或广播条件变量时可以看到的任何内存值也可以被该信号或广播唤醒的任何线程看到。而且,再一次,在信号或广播之后写入的数据可能不一定会被唤醒的线程看到,即使写入发生在唤醒之前。
有关详细信息,另请参阅C++ and Beyond 2012: Herb Sutter - atomic<> Weapons。
【讨论】:
请查看 POSIX 规范的 4.12 部分。
应用程序应确保多个控制线程(线程或进程)对任何内存位置的访问受到限制,这样任何控制线程都无法读取或修改内存位置,而另一个控制线程可能正在修改它。此类访问受限于使用同步线程执行以及相对于其他线程同步内存的函数。 [强调我的]
然后给出一个同步内存的函数列表,以及一些额外的注释。
如果在某些架构上需要内存屏障指令,那么必须使用这些指令。
关于compare_and_swap:这不在POSIX中;检查文档以了解您正在使用的任何内容。例如,IBM defines a compare_and_swap function for AIX 5.3. 没有完整的内存屏障语义文档说明说:
如果 compare_and_swap 用作锁定原语,请在任何关键部分的开头插入 isync。
从这个文档我们可以猜到 IBM 的 compare_and_swap 具有发布语义:因为文档不需要在临界区结束时设置屏障。获取处理器需要发出 isync 以确保它没有读取过时的数据,但发布处理器不需要做任何事情。
在指令级,一些处理器具有比较和交换的某些同步保证,而有些则没有。
【讨论】: