为什么线程同步不需要易失关键字

Why is volatile keyword not needed for thread synchronisation?

本文关键字:易失 关键字 不需要 同步 线程 为什么      更新时间:2023-10-16

我读到volatile关键字不适合线程同步,实际上根本不需要它用于这些目的。

虽然我知道使用这个关键字是不够的,但我不明白为什么它完全没有必要。

例如,假设我们有两个线程,线程 A 只从共享变量读取,线程 B 只写入共享变量。通过例如 pthreads 互斥锁强制执行正确的同步。

IIUC,如果没有 volatile 关键字,编译器可能会查看线程 A 的代码并说:"变量在这里似乎没有被修改,但我们有很多读取;让我们只读取一次,缓存值并优化所有后续读取。它也可能会查看线程 B 的代码并说:"我们在这里对这个变量有很多写入,但没有读取;因此,不需要写入值,因此让我们优化所有写入。

这两种优化都是不正确的。

两者都 会被挥发性阻止。因此,我可能会得出结论,虽然volatile不足以同步线程,但对于线程之间共享的任何变量仍然是必需的。(注意:我现在读到,实际上volatile不需要防止写入省略;所以我不知道如何防止这种不正确的优化(

我知道我在这里错了。但是为什么?

例如,假设我们有两个线程,线程 A 只从共享变量读取,线程 B 只写入共享变量。通过例如 pthreads 互斥锁强制执行正确的同步。

IIUC,如果没有 volatile 关键字,编译器可能会查看线程 A 的代码并说:"变量在这里似乎没有被修改,但我们有很多读取;让我们只读取一次,缓存值并优化所有后续读取。它也可能会查看线程 B 的代码并说:"我们在这里对这个变量有很多写入,但没有读取;因此,不需要写入值,因此让我们优化所有写入。

与大多数线程同步原语一样,pthreads 互斥体操作显式定义了内存可见性语义。

平台要么支持pthreads,要么不支持。如果它支持 pthreads,则支持 pthreads 互斥锁。这些优化要么安全,要么不安全。如果他们是安全的,就没有问题。如果它们不安全,那么任何制造它们的平台都不支持 pthreads 互斥锁。

例如,你说"变量似乎没有在这里被修改",但它确实被修改了——另一个线程可以在那里修改它。除非编译器能够证明其优化不能破坏任何符合标准的程序,否则它无法做到这一点。符合标准的程序可以在另一个线程中修改变量。编译器要么支持 POSIX 线程,要么不支持。

碰巧的是,大多数情况下,大多数情况都会自动发生。编译器只是不知道互斥操作在内部做什么。另一个线程可以做的任何事情,互斥体操作本身都可以做。因此,编译器必须在进入和退出这些函数之前"同步"内存。例如,它不能在调用pthread_mutex_lock时将值保留在寄存器中,因为据它所知,pthread_mutex_lock访问内存中的该值。或者,如果编译器对互斥函数有特殊了解,这将包括了解这些调用中其他线程可访问的缓存值的无效性。

一个需要volatile的平台几乎无法使用。对于对象可能对另一个线程可见或从另一个线程可见的特定情况,您需要每个函数或类的版本。在许多情况下,您几乎只需要使所有内容都volatile,而不是在寄存器中缓存值是无法实现性能的。

你可能已经听过很多次了,volatile 语言中指定的语义不能与线程有效混合。它不仅不够,而且还禁用了许多完全安全且几乎必不可少的优化。

缩短已经给出的答案,您不需要将volatile与互斥体一起使用,原因很简单:

  • 如果编译器知道互斥操作是什么(通过识别pthread_*函数或因为你使用了std::mutex(,它就知道如何处理有关优化的访问(这甚至是std::mutex所必需的(。
  • 如果编译器无法识别它们,则pthread_*函数对其完全不透明,并且涉及任何类型的非本地持续时间对象的优化都不能跨不透明函数

使答案更短,不使用互斥锁或信号量,是一个错误。 一旦线程 B 释放互斥锁(并且线程 A 得到它(,寄存器中包含线程 B 的共享变量值的任何值都保证被写入缓存或内存,这将防止线程 A 运行时出现争用条件并读取此变量。

保证这一点的实现取决于体系结构/编译器。

关键字 volatile 告诉编译器将变量的任何写入或读取视为"可观察的副作用"。这就是它所做的一切。当然,可观察到的副作用不能被优化掉,并且必须按照程序指示的顺序出现在外界面前;编译器可能不会对彼此可观察到的副作用重新排序但是,编译器可以自由地针对不可观察量对它们进行重新排序。因此,volatile 仅适用于访问内存映射硬件、Unix 样式的信号处理程序等。 对于线程间并发,请使用std::atomic或更高级别的同步对象,如 mutexcondition_variablepromise/future