6

我正在编写一个 C++ 应用程序。

我有一个类变量,多个线程正在写入。

在 C++ 中,任何可以在编译器“意识到”它正在被更改的情况下进行修改的东西都需要标记为 volatile 对吗?因此,如果我的代码是多线程的,并且一个线程可能会写入一个 var 而另一个线程从它读取,我是否需要标记 var 易失性?

[我没有竞争条件,因为我依赖于对 int 的写入是原子的]

谢谢!

4

6 回答 6

13

C++ 还没有对多线程的任何规定。在实践中, volatile 并没有达到您的意思(它是为内存寻址硬件设计的,虽然这两个问题相似,但它们的不同之处足以使 volatile 不能做正确的事情——请注意,volatile 已用于其他在 mt 上下文中使用的语言)。

因此,如果您想在一个线程中写入一个对象并在另一个线程中读取它,则必须在需要时使用您的实现所需的同步功能。据我所知, volatile 在其中没有任何作用。

仅供参考,下一个标准将考虑 MT,而 volatile 不会在其中发挥作用。所以这不会改变。您将拥有需要同步的标准定义条件以及实现它们的标准定义方式。

于 2010-02-26T12:16:27.597 回答
4

是的,volatile 是您需要的绝对最小值。它确保代码生成器不会生成将变量存储在寄存器中的代码,并且始终执行对内存的读取和写入。大多数代码生成器可以为与本机 CPU 字大小相同的变量提供原子性保证,它们将确保内存地址对齐,以便变量不能跨越缓存线边界。

然而,这对于现代多核 CPU 来说并不是一个非常强大的合同。Volatile 不保证在另一个内核上运行的另一个线程可以看到对变量的更新。这需要一个内存屏障,通常是一条刷新 CPU 缓存的指令。如果你不提供屏障,线程实际上会一直运行,直到这样的刷新自然发生。这最终会发生,线程调度程序必然会提供一个。这可能需要几毫秒。

一旦你处理了这样的细节,你最终会重新发明一个条件变量(又名事件),它可能不会比线程库提供的更快。或者也经过测试。不要发明你自己的,线程很难做到正确,你不需要不确定非常基本的原语是否可靠的 FUD。

于 2010-02-26T12:44:15.643 回答
3

volatile 指示编译器不要根据变量值或用法的“直觉”进行优化,因为它可以“从外部”进行优化。

但是, volatile 不会提供任何同步,并且您对 int 的写入是原子的假设几乎是现实的!

我想我们需要查看一些用法以了解您的情况是否需要 volatile(或检查程序的行为),或者更重要的是,如果您看到某种同步。

于 2010-02-26T12:15:27.007 回答
1

我认为volatile这只适用于读取,尤其是读取内存映射的 I/O 寄存器。

它可以用来告诉编译器不要假设一旦从内存位置读取值就不会改变:

while (*p)
{
  // ...
}

在上面的代码中,如果*p没有在循环内写入,编译器可能会决定将读取移到循环外,更像这样:

cached_p=*p
while (cached_p)
{
  // ...
}

如果p是一个指向内存映射 I/O 端口的指针,您可能需要在每次进入循环之前检查端口的第一个版本。

如果p是多线程应用程序中指向内存的指针,您仍然不能保证写入是原子的。

于 2010-02-26T13:48:38.453 回答
0

如果没有锁定,您可能仍然会得到编译器或处理器完成的“不可能的”重新排序。并且不能保证对 int 的写入是原子的。

最好使用适当的锁定。

于 2010-02-26T12:15:53.917 回答
-4

挥发性将解决您的问题,即。它将保证系统所有缓存之间的一致性。然而,这将是低效的,因为它将为每个 R 或 W 访问更新内存中的变量。您可能会考虑仅在需要时才使用内存屏障。如果您正在使用或 gcc/icc 已查看同步内置:http: //gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html

编辑(主要是关于 pm100 评论):我知道我的信念不是参考,所以我找到了一些可以引用的东西:)

volatile 关键字旨在防止在存在某些异步事件的情况下可能导致代码不正确的编译器优化例如,如果您将原始变量声明为 volatile,则不允许编译器将其缓存在寄存器中

来自多布博士的

更有意思的 :

易失性场是可线性化的。读取 volatile 字段就像获取锁一样;工作内存失效,易失性字段的当前值从内存中重新读取。写一个 volatile 字段就像释放一个锁: volatile 字段被立即写回内存。 (这都是关于一致性,而不是关于原子性)

来自多处理器编程的艺术,Maurice Herlihy 和 Nir ​​Shavit

Lock 包含内存同步代码,如果你不加锁,你必须做一些事情,使用 volatile 关键字可能是你能做的最简单的事情(即使它是为外部设备设计的,内存绑定到地址空间,这不是重点这里)

于 2010-02-26T12:22:41.217 回答