可以安全地假设使用或传递不可变对象总是线程安全的吗?
答案 0 :(得分:9)
是。如果一个对象是真正不可变的,没有发生内部突变,那么该对象本身将是线程安全的。
(无论你处理对象并以线程安全方式传递它都是另一回事!)
“内部突变”是什么意思?
许多对象似乎是从外部不可变的 - 例如,没有属性设置器或明显触发突变的其他成员 - 但这并不意味着对象的私有内部不是'能够改变。
这就是为什么记录对象和/或其成员的可变性和线程安全性的重要性。否则,您的对象的消费者无法在不仔细检查内部的情况下发现这一点(这是一个实现细节,并且可能随时更改)。
答案 1 :(得分:2)
这取决于你对线程安全的意思;正如Eric Lippert's blog指出的那样,这个词可能意味着几件事。不可变对象保证无论何时访问给定实例上的属性或方法,结果总是相同的;这是该实例的线程安全性。但是,如果你有一个包含对不可变对象的引用的可变字段,则通过相同字段的多个调用可能不是同一个实例,因此不一致(即“线程安全”)。
我怀疑你想到的“线程安全”的含义,答案是肯定的。但请记住,不可变对象不是金子弹;它们不会让你对线程交互免疫,它们只是在单个实例中提供一致性。
答案 2 :(得分:1)
正确回答问题:否。
您不能假设不可变对象总是线程安全。
但最有可能的是。另见关于卢克斯答案的讨论。
例如:访问方法或属性getter可能会触发初始化,调用者不知道。 (例如,延迟初始化)这个初始化必须明确地实现线程安全。
答案 3 :(得分:0)
首先,对象必须是真正不可变的 - 不仅是公共接口,而且所有内部状态必须是初始化的。这禁止例如“获取一次缓存”或延迟初始化。
其次,在构造期间,对象被修改 - 并且由于CPU上的优化和指令重新排序,写入内存的顺序不一定与您在源代码中看到的顺序相同。
这意味着,在没有同步的情况下,另一个线程在完全构造之前可能已经看到对该对象的有效引用。
我对C#memroy模型不够熟悉,无法告诉您哪些同步是必要的 - 也许其他人可以提供帮助(制作社区维基)