为什么线程同步不需要volatile关键字?

时间:2016-02-11 17:19:03

标签: c++ c multithreading volatile

我读到volatile关键字不适合线程同步,事实上根本不需要它。

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

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

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

两种优化都是不正确的。 volatile会阻止 一个。因此,我可能会得出结论,虽然volatile不足以同步线程,但仍然需要在线程之间共享任何变量。 (注意:我现在读到,实际上volatile不需要阻止写入符合;所以我不知道如何防止这种错误的优化)

我明白我错在这里。但为什么呢?

4 个答案:

答案 0 :(得分:9)

  

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

     

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

与大多数线程同步原语一样,pthreads互斥操作具有explicitly defined memory visibility semantics

平台支持pthreads,或者它不支持pthreads。如果它支持pthread,它支持pthreads互斥。要么那些优化是安全的,要么它们不是。如果他们安全,那就没问题了。如果它们不安全,那么任何制作它们的平台都不支持pthreads互斥。

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

实际上,大多数情况都是在大多数平台上自动发生的。只是阻止编译器知道内部互斥操作的作用。另一个线程可以做的任何事情,互斥操作本身都可以做到。所以编译器必须"同步"无论如何进入和退出这些功能之前的记忆。例如,它不能在调用pthread_mutex_lock的整个寄存器中保留一个值,因为它知道所有内容,pthread_mutex_lock访问内存中的该值。或者,如果编译器具有关于互斥函数的特殊知识,那么将包括了解这些调用中其他线程可访问的缓存值的无效性。

需要volatile的平台几乎无法使用。您需要针对特定​​情况的每个函数或类的版本,其中对象可能对另一个线程可见或者从另一个线程可见。在许多情况下,您几乎必须使所有内容volatile而不是在寄存器中缓存值是一种性能不起作用。

正如您可能已多次听到的那样,{C语言中指定的volatile语义只是不会与线程混用。它不仅不够,还会禁用许多完全安全且几乎必不可少的优化。

答案 1 :(得分:4)

缩短已经给出的答案,您不需要将volatile用于互斥锁,原因很简单:

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

答案 2 :(得分:1)

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

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

答案 3 :(得分:0)

关键字volatile告诉编译器将变量的任何write 或read 视为“可观察的副作用”。就是这样。当然,可观察到的副作用不能被优化掉,并且必须按照程序指示的顺序出现在外面世界;编译器可能不会相互重新排序可观察的副作用然而,编译器可以自由地对非可观察对象进行重新排序。因此,volatile仅适用于访问内存映射硬件,Unix风格的信号处理程序等。对于线程间并发,请使用std::atomic或更高级别的同步对象,如mutexcondition_variablepromise/future