2

这是一个普遍的问题,但是:

在多线程程序中,编译器使用寄存器临时存储全局变量是否安全?

我认为不是,因为将全局变量存储在寄存器中可能会更改其他线程的保存值。

那么如何使用寄存器来存储函数中定义的局部变量呢?

我认为没关系,因为没有其他线程能够获取这些变量。

如果我错了,请纠正我。谢谢!

4

3 回答 3

4

事情比你想象的要复杂得多。

即使编译器将值存储到内存中,CPU 通常也不会立即将数据推送到 RAM。它将它存储在缓存中(有些系统在处理器和内存之间有 2 或 3 级缓存)。

更糟糕的是,编译器决定的指令顺序可能不是实际执行的顺序,因为许多处理器可以在自己的管道中重新排序指令(甚至指令的子部分)。

通常,在多线程环境中,您应该亲自注意不要从两个单独的线程访问(读取或写入)相同的内存,除非以下情况之一为真:

  • 您正在使用确保正确同步的几种特殊原子操作之一。
  • 您使用了几种同步操作之一来“保留”对共享数据的访问,然后“放弃”它。这些确实包括所需的内存屏障,这些屏障也可以保证数据是它应该是的。

您可能想阅读http://en.wikipedia.org/wiki/Memory_ordering#Memory_barrier_typeshttp://en.wikipedia.org/wiki/Memory_barrier

如果您已经准备好有点头疼并想看看事情实际上会变得多么复杂,这里是您的晚间讲座内存屏障:软件黑客的硬件视图

于 2012-08-10T15:12:05.730 回答
1

“安全”并不是一个真正合适的词。许多高级语言(例如 C)没有线程模型,因此语言规范没有说明多线程交互。

如果您不使用任何类型的锁定原语,那么您无法保证不同线程如何交互。因此,编译器有权将寄存器用于全局变量。

即使您使用锁定,行为仍然可能很棘手:如果您读取一个变量,然后获取一个锁,然后再次读取该变量,编译器仍然无法知道它是否必须再次从内存中读取变量,或者可以使用它存储在寄存器中的较早的值。

在 C/C++ 中,将变量声明为 volatile 将强制编译器始终从内存重新加载变量并解决此特定实例。

大多数系统上还有“互锁*”原语,它们具有保证的原子性语义,可用于确保某些操作是线程安全的。锁定原语通常建立在这些低级操作之上。

于 2012-08-10T15:10:25.820 回答
0

在多线程程序中,您有两种情况之一:如果它在单处理器(单核,单 CPU)上运行,那么线程之间的切换就像进程之间的切换一样处理(尽管由于线程在相同的虚拟内存空间) - 一个线程的所有寄存器在转换到另一个线程期间被保存,因此无论出于何种目的使用寄存器都可以。这是操作系统使用的上下文切换例程的工作,寄存器集被认为是线程(或进程)上下文的一部分。如果您有一个多处理器系统 - 多个 CPU 或单个 CPU 上的多个内核 - 每个处理器都有自己不同的一组寄存器,所以同样,使用寄存器来存储东西是可以的。最重要的是,当然,

也就是说,在某些架构和/或某些操作系统上,可能存在特定的例外情况,因为 ABI 保留了某些寄存器供操作系统或提供操作系统接口的库的特定用途使用,但是您的编译器(s)通常具有内置平台的那种类型的知识。但是,如果您正在进行内联汇编或某些其他“低级”事情,则需要了解它们......

于 2012-08-10T15:08:28.673 回答