我读到volatile
关键字不适合线程同步,实际上,这些目的根本不需要它。
虽然我知道使用此关键字是不够的,但我无法理解为什么它完全没有必要。
例如,假设我们有两个线程,线程A仅从共享变量读取,线程B仅写入共享变量。通过例如适当的同步强制执行pthreads互斥锁。
IIUC,如果没有volatile关键字,则编译器可能会查看线程A的代码,并说:“该变量在这里似乎没有被修改,但是我们可以读取很多内容;让我们只读取一次,缓存值并优化所有后续读取。”它还可能会查看线程B的代码,并说:“我们在这里对该变量进行了很多写入操作,但没有读取操作;因此,不需要写入值,因此,我们可以优化所有写入操作。”
两种优化都是不正确的。而且volatile可以阻止两个和一个。因此,我可能会得出这样的结论:尽管volatile
不足以同步线程,但是仍然需要线程之间共享的任何变量。 (注意:我现在读到,实际上并不需要volatile
来防止写省略;因此我不知道如何防止这种不正确的优化)
我了解我在这里错了。但为什么?
最佳答案
像大多数线程同步原语一样,pthreads互斥操作具有explicitly defined memory visibility semantics。
该平台是否支持pthreads或不支持。如果支持pthread,则支持pthread互斥。这些优化是安全的,或者不是。如果他们很安全,那没有问题。如果它们不安全,那么任何使它们不安全的平台都不支持pthreads互斥体。
例如,您说“该变量似乎未在此处进行修改”,但确实如此-另一个线程可以在此处对其进行修改。除非编译器能够证明其优化不会破坏任何符合要求的程序,否则它不会成功。并且一致的程序可以在另一个线程中修改变量。编译器是否支持POSIX线程,要么不支持。
碰巧的是,大多数情况会在大多数平台上自动发生。只是阻止了编译器对互斥操作在内部执行的操作有任何了解。另一个线程可以执行的任何操作,互斥操作本身都可以执行。因此,无论如何,在进入和退出这些功能之前,编译器必须“同步”内存。例如,它无法在对pthread_mutex_lock
的调用中将值保留在寄存器中,因为就其所知,pthread_mutex_lock
会在内存中访问该值。或者,如果编译器对互斥函数有特殊的了解,那么这将包括了解跨这些调用的其他线程可访问的缓存值的无效性。
需要volatile
的平台将几乎无法使用。对于特定的情况,您可能需要每个函数或类的版本,在某些特定情况下,某个对象可能对另一个线程可见或从另一个线程可见。在许多情况下,您几乎只需要将所有内容设置为volatile
,而不将值缓存在寄存器中就不会提高性能。
您可能已经听过很多次了,C语言中指定的volatile
的语义只是无法与线程有效地混合使用。这不仅不够,而且还会禁用许多完全安全且几乎必不可少的优化。