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 不会重新排序写入?
这里有一个偷懒的方法:回避问题,回避问题。例如,只有在有两个“事物”时才需要考虑重新排序——其中一个“事物”永远不会出现故障。您可以牺牲哨兵值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
保证读取和写入是原子的,这也有帮助。