我们保证通过数据竞争的哈希码缓存能否正常工作?

Val*_*zub 7 .net concurrency volatile memory-model

public class TestHashRace
{
    private int cachedHash = 0;
    private readonly object value;

    public object Value
    {
        get { return value; }
    }

    public TestHashRace(object value)
    {
        this.value = value;
    }

    public override int GetHashCode()
    {
        if (cachedHash == 0) {
            cachedHash = value.GetHashCode();
        }
        return cachedHash;
    }

    //Equals isn't part of the question, but since comments request it, here we go:
    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != GetType()) return false;
        return Equals((TestHashRace) obj);
    }

    protected bool Equals(TestHashRace other)
    {
        return Equals(value, other.value);
    }
}
Run Code Online (Sandbox Code Playgroud)

这是简单的测试类.

我们保证GetHashCode会永远返回相同的价值吗?如果我们是,有人可以指出一些给我们这种保证的参考资料吗?

我们不担心它是否多次计算我们的值的哈希码,我们只是想确保返回的值总是相同的.

我们的类必须是不可变的,而cachedHash字段是可变的.由于性能原因,Field不能变化(我们在这里质疑这个问题和优化的整个想法).价值是不可改变的.它必须是线程安全的.

对于某些特定值,当它为0时,我们可以使用潜在的哈希码重新计算.我们不希望使用可空类型或为内存原因添加其他字段(如果我们只存储1个int,则使用更少的内存),因此它必须是一个int字段来处理哈希码问题.

das*_*ght 6

我们是否保证GetHashCode将始终返回相同的值?

不可以.保证仅适用于value具有正确实现GetHashCode方法的不可变对象.可变对象在其内容发生变异时可能会更改其哈希码(这就是为什么不应将可变对象用作哈希键的原因).

即使它TestHashRace本身是不可变的,也是如此,因为你可以这样做:

var evil = new StringBuilder("hello");
var thr = new TestHashRace(evil);
RunConcurrentCode(thr);
evil.Append(", world!");
Run Code Online (Sandbox Code Playgroud)

如果多个线程同时RunConcurrentCode启动对thrs 的调用GetHashCode,然后在不同的侧面完成Append,则返回的数字value.GetHashCode可能不同.

[ 编辑: ]值是不可变的

然后,为保证持有所要求的唯一的事情就是valueGetHashCode正确实施,即不使用随机的东西等.

注:由于零是哈希码合法的值,你的代码可能会重复调用valueGetHashCode时候实际的代码是零.解决这个问题的一种方法是使用nullable cachedHash:

int? cachedHash;
...
public override int GetHashCode() {
    return cachedHash ?? (cachedHash = value.GetHashCode());
}
Run Code Online (Sandbox Code Playgroud)