pthread_mutex_lock是否包含内存屏障指令?

17

pthread_mutex_lockpthread_mutex_unlock函数是否调用内存栅栏/屏障指令?或者较低级别的指令如compare_and_swap是否隐式包含内存屏障?

2个回答

19

pthread_mutex_lock和pthread_mutex_unlock函数会调用内存屏障/栅栏指令吗?

它们会,线程创建也是一样。

然而需要注意的是,内存屏障有两种类型:编译器和硬件。

编译器屏障只能防止编译器重新排列读写顺序和猜测变量值,但不能防止CPU重新排序。

硬件屏障可以防止CPU重新排序读写操作。完整的内存屏障通常是最慢的指令,大多数时候你只需要具备获取和释放语义的操作(以实现自旋锁和互斥锁)。

在多线程中,你大多数情况下都需要这两种屏障。

任何在此翻译单元中未提供定义(且不是内部函数)的函数都是编译器屏障。pthread_mutex_lock、pthread_mutex_unlock、pthread_create也会发出硬件内存屏障以防止CPU重新排序读写操作。

引自David R. Butenhof的《使用POSIX线程编程》。

Pthreads提供了一些关于内存可见性的基本规则。您可以指望标准的所有实现都遵循这些规则:

  1. 当调用pthread_create时,线程可以看到的任何内存值也可以在新线程启动时被看到。在调用pthread_create之后写入内存的任何数据可能不会被新线程看到,即使写入发生在线程启动之前。

  2. 当一个线程解锁互斥量时,无论是直接解锁还是等待条件变量,该线程可以看到的任何内存值也可以被稍后锁定同一互斥量的任何线程看到。同样,在解锁互斥量之后写入的数据可能不会被锁定互斥量的线程看到,即使写入发生在锁定之前。

  3. 当一个线程通过取消、从其启动函数返回或调用pthread_exit终止时,可以看到的任何内存值也可以被通过调用pthread_join与已终止线程连接的线程看到。当然,线程终止后写入的数据可能不会被连接的线程看到,即使写入发生在连接之前。

  4. 当一个线程发出信号或广播条件变量时,可以看到的任何内存值也可以被该信号或广播唤醒的任何线程看到。同样,在发出信号或广播之后写入的数据可能不会被唤醒的线程看到,即使写入发生在唤醒之前。

此外,更多详细信息请参见C++ and Beyond 2012: Herb Sutter - atomic<> Weapons

15
请查看POSIX规范的第4.12节。
应用程序应确保通过多个控制线程(线程或进程)访问任何内存位置受到限制,以使没有控制线程可以在另一个控制线程可能正在修改它的内存位置时读取或修改该内存位置。使用同步线程执行的函数限制此类访问,并且还与其他线程同步内存。[强调我的]
然后列出了同步内存的函数及一些附加说明。
如果某些架构需要内存屏障指令,则必须使用这些指令。
关于 compare_and_swap:POSIX标准中没有此函数;请查看您所使用的文档。例如,IBM 为AIX 5.3定义了一个compare_and_swap函数。 但该函数不具有完整的内存屏障语义。文档注释如下:

如果将 compare_and_swap 用作锁定原语,请在任何关键部分的开头插入 isync。

从这个文档中我们可以猜测 IBM 的 compare_and_swap 具有释放语义:因为文档不要求在关键部分结束时使用屏障。获取处理器需要发出 isync 以确保其不会读取过期数据,但发布处理器则无需执行任何操作。

在指令级别上,一些处理器具有带有某些同步保证的比较和交换,而有些则没有。


2
很好的对POSIX的参考,我不知道POSIX规定了确切的函数列表。+1 - Maxim Egorushkin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接