.NET 中的后续写入是否可以由运行时或处理器重新排序?

bra*_*ing 5 c# memory-model thread-safety

我有不可变的对象,我希望延迟计算其哈希码。我已经实施了

private bool _HasHashCode = false;
private int _HashCode;
public override int GetHashCode()
{
    if (_HasHashCode)
        return _HashCode;

    long hashCode;
    unchecked
    {
        hashCode = Digits;
        hashCode = (hashCode*397) ^ XI;
        hashCode = (hashCode*397) ^ YI;
        hashCode = (int) ( hashCode % Int32.MaxValue);
    }

    // is it possible that these two write instructions
    // get reordered on a certain .NET/CPU architecture 
    // combination:

    _HashCode = (int)hashCode;
    _HasHashCode = true;

    return _HashCode;
}
Run Code Online (Sandbox Code Playgroud)

我的推理是,32 位 _HashCode 成员是 32 位,写入它是原子的,因此即使由于设置 _HasHashCode 属性时的竞争条件而运行两次计算也没关系,因为每次都会计算相同的值。

我担心 CLR 可能会对_HashCode_HasHashCode的写入重新排序。这是一个问题还是我可以确定 CLR 不会重新排序写入?

Mar*_*ell 4

这里有一个偷懒的方法:回避问题,回避问题。例如,只有在有两个“事物”时才需要考虑重新排序——其中一个“事物”永远不会出现故障。您可以牺牲哨兵值0来表示“尚未计算” - 然后作为计算的最后一步,避免哨兵:

int hash;
public override int GetHashCode()
{
    var snapshot = hash;
    if(snapshot == 0) // means: not yet calculated
    {
        // snapshot = ... your actual implementation

        if(snapshot == 0) snapshot = -124987; // avoid sentinel value
        hash = snapshot;
    }
    return snapshot;
}
Run Code Online (Sandbox Code Playgroud)

请注意,int保证读取和写入是原子的,这也有帮助。