c++易失性多线程变量

发布于 2024-08-23 14:31:21 字数 221 浏览 10 评论 0原文

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

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

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

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

谢谢!

I'm writing a C++ app.

I have a class variable that more than one thread is writing to.

In C++, anything that can be modified without the compiler "realizing" that it's being changed needs to be marked volatile right? So if my code is multi threaded, and one thread may write to a var while another reads from it, do I need to mark the var volaltile?

[I don't have a race condition since I'm relying on writes to ints being atomic]

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

大姐,你呐 2024-08-30 14:31:21

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

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

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

C++ hasn't yet any provision for multithreading. In practice, volatile doesn't do what you mean (it has been designed for memory adressed hardware and while the two issues are similar they are different enough that volatile doesn't do the right thing -- note that volatile has been used in other language for usages in mt contexts).

So if you want to write an object in one thread and read it in another, you'll have to use synchronization features your implementation needs when it needs them. For the one I know of, volatile play no role in that.

FYI, the next standard will take MT into account, and volatile will play no role in that. So that won't change. You'll just have standard defined conditions in which synchronization is needed and standard defined way of achieving them.

神回复 2024-08-30 14:31:21

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

然而,对于现代多核 CPU 来说,这并不是一个非常强有力的契约。 Volatile 不承诺在另一个核心上运行的另一个线程可以看到变量的更新。这需要内存屏障,通常是刷新 CPU 缓存的指令。如果不提供屏障,线程实际上将继续运行,直到自然发生这种刷新。这最终会发生,线程调度程序必然会提供一个。这可能需要几毫秒。

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

Yes, volatile is the absolute minimum you'll need. It ensures that the code generator won't generate code that stores the variable in a register and always performs reads and writes from/to memory. Most code generators can provide atomicity guarantees on variables that have the same size as the native CPU word, they'll ensure the memory address is aligned so that the variable cannot straddle a cache-line boundary.

That is however not a very strong contract on modern multi-core CPUs. Volatile does not promise that another thread that runs on another core can see updates to the variable. That requires a memory barrier, usually an instruction that flushes the CPU cache. If you don't provide a barrier, the thread will in effect keep running until such a flush occurs naturally. That will eventually happen, the thread scheduler is bound to provide one. That can take milliseconds.

Once you've taken care of details like this, you'll eventually have re-invented a condition variable (aka event) that isn't likely to be any faster than the one provided by a threading library. Or as well tested. Don't invent your own, threading is hard enough to get right, you don't need the FUD of not being sure that the very basic primitives are solid.

标点 2024-08-30 14:31:21

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

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

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

volatile instruct the compiler not to optimize upon "intuition" of a variable value or usage since it could be optimize "from the outside".

volatile won't provide any synchronization however and your assumption of writes to int being atomic are all but realistic!

I'd guess we'd need to see some usage to know if volatile is needed in your case (or check the behavior of your program) or more importantly if you see some sort of synchronization.

硬不硬你别怂 2024-08-30 14:31:21

我认为 易失性仅适用于读取,尤其是读取内存映射的 I/O 寄存器。

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

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

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

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

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

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

I think that volatile only really applies to reading, especially reading memory-mapped I/O registers.

It can be used to tell the compiler to not assume that once it has read from a memory location that the value won't change:

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

In the above code, if *p is not written to within the loop, the compiler might decide to move the read outside the loop, more like this:

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

If p is a pointer to a memory-mapped I/O port, you would want the first version where the port is checked before the loop is entered every time.

If p is a pointer to memory in a multi-threaded app, you're still not guaranteed that writes are atomic.

爱情眠于流年 2024-08-30 14:31:21

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

最好使用适当的锁定。

Without locking you may still get 'impossible' re-orderings done by the compiler or processor. And there's no guarantee that writes to ints are atomic.

It would be better to use proper locking.

梓梦 2024-08-30 14:31:21

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

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

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

来自 Dobb 博士的

更有趣:

易失性字段是可线性化的。读取易失性字段就像获取锁;工作存储器无效,并且从存储器中重新读取易失性字段的当前值。写入易失性字段就像释放锁:易失性字段会立即写回内存。
(这完全是关于一致性,而不是关于原子性)

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

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

Volatile will solve your problem, ie. it will guarantee consistency among all the caches of the system. However it will be inefficiency since it will update the variable in memory for each R or W access. You might concider using a memory barrier, only whenever it is needed, instead.
If you are working with or gcc/icc have look on sync built-ins : http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html

EDIT (mostly about pm100 comment):
I understand that my beliefs are not a reference so I found something to quote :)

The volatile keyword was devised to prevent compiler optimizations that might render code incorrect in the presence of certain asynchronous events. For example, if you declare a primitive variable as volatile, the compiler is not permitted to cache it in a register

From Dr Dobb's

More interesting :

Volatile fields are linearizable. Reading a volatile field is like acquiring a lock; the working memory is invalidated and the volatile field's current value is reread from memory. Writing a volatile field is like releasing a lock : the volatile field is immediately written back to memory.
(this is all about consistency, not about atomicity)

from The Art of multiprocessor programming, Maurice Herlihy & Nir Shavit

Lock contains memory synchronization code, if you don't lock, you must do something and using volatile keyword is probably the simplest thing you can do (even if it was designed for external devices with memory binded to the address space, it's not the point here)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文