Multithreading 多线程程序中编译器对寄存器的使用

Multithreading 多线程程序中编译器对寄存器的使用,multithreading,process,operating-system,Multithreading,Process,Operating System,这是一个一般性问题,但: 在多线程程序中,编译器使用寄存器临时存储全局变量是否安全 我认为不是,因为在寄存器中存储全局变量可能会更改保存的值 对于其他线程 使用寄存器存储函数中定义的局部变量怎么样 我认为这是可以的,因为没有其他线程能够获得这些变量 如果我错了,请纠正我。 谢谢大家! 在多线程程序中,有两种情况之一:如果它在单处理器(单核、单CPU)上运行,那么线程之间的切换就像进程之间的切换一样处理(尽管由于线程在相同的虚拟内存空间中运行,所以工作量不大)-一个线程的所有寄存器都在转换到另一个

这是一个一般性问题,但:


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

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

使用寄存器存储函数中定义的局部变量怎么样

我认为这是可以的,因为没有其他线程能够获得这些变量

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

在多线程程序中,有两种情况之一:如果它在单处理器(单核、单CPU)上运行,那么线程之间的切换就像进程之间的切换一样处理(尽管由于线程在相同的虚拟内存空间中运行,所以工作量不大)-一个线程的所有寄存器都在转换到另一个线程的过程中保存,因此无论出于何种目的使用寄存器都可以。这是操作系统使用的上下文切换例程的工作,寄存器集被视为线程(或进程)上下文的一部分。如果您有一个多处理器系统——多个CPU或单个CPU上的多个内核——每个处理器都有自己独特的寄存器集,所以同样,使用寄存器存储东西是可以的。当然,除此之外,在特定CPU上切换上下文将在切换到新线程/进程之前保存旧线程/进程的寄存器,因此所有内容都会保留下来


也就是说,在某些体系结构和/或某些操作系统上,可能存在特定的例外情况,因为ABI保留某些寄存器供操作系统或提供操作系统接口的库使用,但您的编译器通常具有内置平台的这类知识。但是,如果您正在进行内联汇编或某些其他“低级”操作,则需要注意这些问题。

“安全”一词并不适合使用。许多高级语言(例如C)没有线程模型,因此语言规范没有提到多线程交互

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

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

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


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

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

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

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

一般来说,在多线程环境中,您个人应注意不要从两个单独的线程访问(读或写)同一内存,除非以下情况之一为真:

  • 您使用的是确保正确同步的几个特殊原子操作之一
  • 您使用了几个同步操作中的一个来“保留”对共享数据的访问,然后“放弃”它。这些确实包括所需的内存屏障,也保证了数据的真实性
你可能想要阅读和阅读


如果你已经准备好了一点头痛,想看看事情到底有多复杂,这是你的晚间讲座。

当你提到“线程”时,你应该想到JVM或.NET内存模型之类的共享内存模型。此外,如果您将本地值保存到寄存器,则根本不安全,因为处理器之间使用了寄存器。如果您使用register并且从不通过直接保存到register来释放值,这将非常复杂。保存寄存器时,是保存其值,还是保存寄存器本身?确切的意思是哪个寄存器?如果是保存的值,则为ok。如果保存的是精确的寄存器(单核单CPU),我们会不会遇到问题?它会将每个寄存器包含的值保存到内存中的特定位置,以便下次该线程计划再次运行时可以恢复。你不能真正“保存”一个寄存器,因为它是集成电路的一部分…我的意思是保存一个指向特定寄存器的指针。。。假设我想在R13中保存一个特定的数字。据我所知,我们不会保存R13,我们将保存10。我说的对吗?它将保存R13中的任何值-如果R13中的值是10,那么值10将被放入一个特定的内存位置,以便稍后调用。C11和C++11引入了线程感知内存模型。(数据竞赛是未定义的行为,正好允许编译器继续像以前一样保持寄存器中的值)C++<代码> STD::原子和C11< C++ >原子< /代码>允许便携式无锁代码,使易挥发< /代码>手工轧制原子过时。(volatile只能半可移植地为您提供诸如
memory\u order\u relaxed
——基本上现在C++11已经存在了。)但是,是的,
std::atomic
确实强制实际访问内存,就像您描述的那样,并且还对非atom的约束进行排序