如果是这样,不同的线程将如何共享表示对象的同一实例或内存块?不同的线程是否会以某种方式"复制"单实例对象方法代码以在其自己的 CPU 资源上运行?
编辑 - 进一步澄清这个问题: 我知道不同的线程可以同时执行单例对象方法的"进程",而它们可能并不都在主动执行 - 它们可能正在等待操作系统调度执行,并在方法内处于各种执行状态。此问题专门针对在不同处理器上执行每个线程的多个活动线程。多个线程是否可以同时主动执行相同的确切代码路径(相同的内存区域(?
单例对象是否可以同时在不同的线程中执行(线程安全(方法?
是的,当然。请注意,这与在同一个非单一实例对象实例上运行方法的多个线程没有什么不同。
不同的线程将如何共享表示对象的同一实例或内存块?
(忽略 NUMA 和处理器缓存(,对象仅存在于内存中的一个位置(因此,它是一个单一实例(,因此不同的线程都从相同的内存地址读取。
现在,如果对象是不可变的(并且没有外部副作用,如 IO(,那么没关系:多个线程从同一内存读取并且从不更改它不会引入任何问题。
以此类推,这就像在桌子上放一张(单面(纸,10个人同时阅读:没问题。
如果对象是可变的或(确实有外部副作用,如IO(,那么这是一个问题:)您需要同步每个线程的更改,否则它们将相互覆盖 - 这很糟糕。
请注意,在许多语言和平台(如 C#/.NET 和 C++(中,文档通常可以断言或声明某个方法是"线程安全的",但这不一定由运行时绝对保证(除非函数可证明"const
正确"并且没有 IO,这是C++可以做的事情,但 C# 目前无法做到这一点(。
请注意,仅仅因为单个方法是线程安全的,并不意味着整个逻辑操作(例如按顺序调用多个对象方法(是线程安全的;例如,考虑一个Dictionary<K,V>
:许多线程可以同时调用TryGetValue
而没有问题 - 但是一旦一个线程调用.Add
那么就会搞砸所有其他调用TryGetValue
线程(因为Dictionary<K,V>
不能保证.Add
是原子的并且阻塞TryGetValue
, 这就是为什么我们使用具有不同 API 的ConcurrentDictionary
- 或者我们将逻辑业务/域操作包装在lock
(Monitor
( 语句中(。
不同的线程是否会以某种方式"复制"单实例对象方法代码以在其自己的 CPU 资源上运行?
不。
你可以通过[ThreadLocal]
和[ThreadStatic]
(以及[AsyncLocal]
(来实现这一点,但是函数式编程技术的支持者(如我自己(会出于多种原因强烈推荐不要这样做。如果您希望线程"拥有"某些内容,则将堆栈上的对象作为参数传递,而不是在隐藏的静态状态中传递。
(另请注意,这不适用于值类型(例如struct
(,它们总是在使用之间复制,但我假设您指的是class
对象实例,fwiw - 因为 .NET 无论如何都不鼓励使用可变结构(。