我有这个问题。
public class Foo : object
{
public override bool Equals(obj a, objb)
{
return ((Foo)a).Bar.GetHashCode() == ((Foo)b).Bar.GetHashCode();
}
}
假设我想让Foo线程安全。我是否需要同步调用GetHashCode()?
答案 0 :(得分:2)
即使你要添加同步,你会同步什么?
我会说作为一般规则GetHashCode
应该始终是线程安全的,但文档并未声明线程安全的要求。 Bar
的消费者无法保证它的使用是线程安全的,除非Bar
在内部提供线程安全。
假设Bar
使用两个内部字段来计算哈希码。如果另一个线程正在更新字段且只有一个已更改,那么在更新一个字段之后但在第二个字段更新之前,您将能够获得GetHashCode()
结果,除非内部Bar
提供同步。
答案 1 :(得分:2)
来自System.Object
的文档:
此类型的公共静态成员是线程安全的。实例成员不保证是线程安全的。
因此,如果您没有覆盖Object.GetHashCode
,则无法保证线程安全。如果你有覆盖它取决于你的实施。它还取决于你所说的“thread safe。”
答案 2 :(得分:0)
这实际上取决于Bar是什么。如果bar是Foo上的“只读”字符串字段,那么它是线程安全的。如果bar是一个读写字段,其中包含一个具有自己定义的GetHashCode()的任意值类型,则根本不存在关于您的函数的线程安全。
这还取决于您对线程安全性的定义。您是否定义线程安全性以表示返回值始终一致?如果是这样,那么除非Bar的哈希码的值不能改变,否则无法保证。如果没有,那么你必须承认这个方法的返回值可能是错误的,因为它甚至在返回之前的a和b的当前状态。
如果Bar可以改变,那么这可能是一个棘手的问题。如果您尝试锁定a和b(或锁定a和b中的私有互斥锁),您执行哪个顺序?如果呼叫者反转参数的顺序怎么办?这将造成非常直接的僵局。谷歌“锁定层次结构”的概念,以查看其中的危险。如果Bar可以更改,则使此操作成为线程安全的唯一方法是锁定对所有Bar的写访问权限,这些Bar可以在进行比较之前使用一个全局锁更改。
答案 3 :(得分:0)
我看不出一个令人信服的理由,为什么你需要让GetHashCode
线程安全,我可以看到它导致死锁或至少锁定车队如果你不小心; GetHashCode
来自很多你可能不会想到的地方。
更合乎逻辑的是,如果您有多个线程共享一个Foo
,那些线程将需要同步他们对Foo
的访问权限,因此您只需要' t有多个线程一次调用GetHashCode
(或Foo
的任何其他方法)。
正确设计线程安全类可能是一项艰难的工作;除非你有充分的理由要求线程安全的实例成员,否则我会选择不这样做。绝大多数.NET Framework本身没有线程安全的实例成员。
最后,确实没有类通用线程安全的东西。您可以同步每个实例成员,但是如果他们不理解语义,那么您的类的使用者仍然可以搞砸它。使GetHashCode
线程安全意味着您正在尝试考虑每个可想到的线程场景,但即使是大多数线程安全的类也只是某些操作的线程安全,而GetHashCode
通常不是其中之一。
我会把你推荐给Eric Lippert最近的post on thread-safety;在使用诸如“Make Foo
线程安全的”这样的短语时,请考虑其内容。