GetHashCode线程安全吗?

时间:2010-02-10 03:46:53

标签: c# .net thread-safety

我有这个问题。

public class Foo : object
{
    public override bool Equals(obj a, objb)
    {
       return ((Foo)a).Bar.GetHashCode() == ((Foo)b).Bar.GetHashCode();
}

}

假设我想让Foo线程安全。我是否需要同步调用GetHashCode()?

4 个答案:

答案 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线程安全的”这样的短语时,请考虑其内容。

相关问题