编译器在多线程程序中使用寄存器

时间:2012-08-10 14:50:18

标签: multithreading process operating-system

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

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

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

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

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

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

3 个答案:

答案 0 :(得分:2)

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

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

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

一般情况下,在多线程环境中,除非满足下列条件之一,否则您应该小心从不从两个单独的线程访问(读取或写入)相同的内存:

  • 您正在使用几种确保正确同步的特殊原子操作之一。
  • 您已使用多个同步操作之一来“保留”对共享数据的访问权限,然后“放弃”它。这些确实包括所需的内存障碍,这些障碍也可以保证数据符合预期。

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

如果你已经准备好有点头痛并且想看看事情有多复杂,那么这是你的晚间讲座Memory Barriers: a Hardware View for Software Hackers

答案 1 :(得分:1)

'安全'并不是真正合适的词汇。许多更高级别的语言(例如C)没有线程模型,因此语言规范没有提及多线程交互。

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

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

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

在大多数系统上还有'Interlocked *'原语,它们保证了原子性语义,可以用来确保某些操作是线程安全的。锁定原语通常建立在这些低级操作上。

答案 2 :(得分:0)

在多线程程序中,您有以下两种情况之一:如果它在单处理器(单核,单CPU)上运行,那么在线程之间切换就像处理进程之间的切换一样(尽管从线程开始并没有那么多工作)在同一虚拟内存空间中运行) - 在转换到另一个线程期间保存一个线程的所有寄存器,因此无论出于何种目的使用寄存器都可以。这是OS使用的上下文切换例程的作用,并且寄存器集被视为线程(或进程)上下文的一部分。如果你有一个多处理器系统 - 在一个CPU上有多个CPU或多个内核 - 每个处理器都有自己独特的寄存器组,那么再次使用寄存器来存储东西也很好。当然,最重要的是,特定CPU上的上下文切换将在切换到新线程/进程之前保存旧线程/进程的寄存器,因此保留所有内容。

也就是说,在某些体系结构和/或某些操作系统上,可能存在特定的例外情况,因为某些寄存器由ABI保留用于操作系统或提供操作系统接口的库的特定用途,但是你的编译器通常都有你内置平台的那种知识。但是,你需要知道它们,如果你正在进行内联汇编或某些其他“低级”事情......