单例对象能否具有在不同线程中同时执行的(线程安全)方法?

时间:2019-10-09 20:27:20

标签: multithreading multiprocessing

如果是这样,不同的线程将如何共享代表该对象的相同实例或内存块?不同线程会以某种方式“复制”单实例对象方法代码以在其自己的CPU资源上运行吗?

编辑-为了进一步阐明这个问题: 我了解不同的线程可能处于同一时间执行单例对象的方法的“过程”中,尽管它们可能不是全部都在积极执行-它们可能正在等待被操作系统调度并处于各种执行状态在方法内。这个问题专门针对在不同处理器上执行每个活动线程的多个活动线程。多个线程可以同时主动执行相同的确切代码路径(内存的相同区域)吗?

1 个答案:

答案 0 :(得分:0)

  

单例对象能否具有在不同线程中同时执行的(线程安全)方法?

是的,当然。请注意,这与在同一非单一对象实例上运行方法的多个线程没有什么不同。

  

不同的线程将如何共享表示对象的相同实例或内存块?

(忽略NUMA和处理器缓存),该对象仅存在于内存中的一个位置(因此,它是一个单例),因此不同的线程都从同一内存地址读取。

现在,如果对象是不可变的(并且没有像IO这样的外部副作用),那就没关系:多个线程从同一内存读取并且从不更改它不会带来任何问题。

打个比方,这就像在一张桌子上放一张纸(单面),然后有10个人同时阅读它:没问题。

如果对象是可变的或(确实具有IO等外部副作用),那就是一个问题:)您需要同步每个线程的更改,否则它们将彼此覆盖-这很糟糕。

请注意,在许多语言和平台(例如C#/。NET和C ++)中,文档通常可以断言或声称方法是“线程安全的”,但这不一定是运行时绝对保证的(除非函数证明是“ {const-正确的”并且没有IO,这是C ++可以做的,但是C#目前无法做到)。

请注意,仅因为单个方法是线程安全的,并不意味着整个逻辑操作(例如,依次调用多个对象方法)就是线程安全的;例如,考虑一个Dictionary<K,V>:许多线程可以同时调用TryGetValue而没有问题-但是,只要一个线程调用.Add,就会使所有其他调用{{1}的线程陷入混乱}(因为TryGetValue不保证Dictionary<K,V>是原子的并且阻塞.Add,这就是为什么我们使用具有不同API的TryGetValue的原因,或者我们包装逻辑业务/ ConcurrentDictionarylock)语句中的域操作。

  

不同的线程会以某种方式“复制”单实例对象方法代码以在其自己的CPU资源上运行吗?

否。

您可以使用Monitor[ThreadLocal](以及[ThreadStatic])来实现这一点,但是出于许多原因,函数式编程技术的支持者(如我本人)将强烈反对这样做。如果您希望线程“拥有”某些东西,则可以将对象作为参数而不是隐藏的静态状态传递给堆栈。

(还要注意,这不适用于总是在使用之间复制的值类型(例如[AsyncLocal]),但是我假设您是指struct对象实例fwiw-作为.NET不鼓励使用可变结构。