Can a subsequent .NET entry be reordered by the runtime or processor?

I have immutable objects whose hash code I want to calculate lazily. I implemented

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;
}

My reasoning is that the 32-bit _HashCode element is 32 bits and writing to it is atomic, so even if the calculation is performed twice due to race conditions when setting the _HasHashCode property , this is not because the same value will be calculated each time.

My concern is that the CLR may change the write order to _HashCode and _HasHashCode . Is this a problem or am I sure the CLR is not reordering records?

+4
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;
}

, int , .

+3

, - : JIT.

MSDN CLR ( ). ( . 2 .)

volatile. , Thread.MemoryBarrier() :

_HashCode = (int)hashCode;
Thread.MemoryBarrier(); // Prevents reordering of the statements before and after.
_HasHashCode = true;

A MemoryBarrier , .

, microsoft:

MemoryBarrier (, , Intel Itanium).

, , , , - ( GetHashCode()).

, .

+2

: @Groo reordering instructions ( CLR), . , lock , , reordering instructions. this, , "Monitor.Enter Monitor.Exit ".

; :

private bool _HasHashCode = false;
private int _HashCode;
private readonly object _lock = new object();

public override int GetHashCode()
{
    if (_HasHashCode)
        return _HashCode;

    lock (_lock)
    {
        if (_HasHashCode)
            return _HashCode;

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

        _HashCode = (int) hashCode;
        _HasHashCode = true;
        return _HashCode;
    }
}

/ , , - " ?". . lock , ( - !). . , ( ), _HasHashCode true .

, , ; ! lock ( ).

: . : , ?:)

+1

, , :

Comparison of possible instruction reorderings on different architectures

(: Linux, . )

Intel OP, , :

  • x86 ( IA-32 Intel64 Intel x86-64, IA-64/Itanium),

  • IA-64 (Itanium).

, ,.NET( 2.0) , - ( ):

.NET(...) . , .NET Framework 2.0, CLR. . - , JR CLRs ( , IA64 /),

MSDN :

2:.NET Framework 2.0

( .NET 2.0):

  • , ECMA, , ECMA .
  • .
  • , , . , . 5 , .
  • .
  • , .

Given the fact that Microsoft recently abandoned Itanium support in Windows Server and Visual Studio, you can only focus on x86 / x64 now, for which the more stringent memory models mentioned above prohibit out-of-order writing.

Of course, since there are various implementations of Microsoft.NET (Mono), such statements must be taken with reserve.

0
source

All Articles