Thread safe DateTime update using Interlocked.*

前端 未结 4 1021
生来不讨喜
生来不讨喜 2021-02-12 22:40

Can I use an Interlocked.* synchronization method to update a DateTime variable?

I wish to maintain a last-touch time stamp in memory. Multiple http threads will update

4条回答
  •  野的像风
    2021-02-12 23:19

    Option 1: use a long with Interlocked and DateTime.ToBinary(). This doesn't need volatile (in fact you'd get a warning if you had it) because Interlocked already ensures an atomic update. You get the exact value of the DateTime this way.

    long _lastHit;
    
    void Touch()
    {
        Interlocked.Exchange(ref _lastHit, DateTime.Now.ToBinary());
    }
    

    To read this atomically:

    DateTime GetLastHit()
    {
        long lastHit = Interlocked.CompareExchange(ref _lastHit, 0, 0);
        return DateTime.FromBinary(lastHit);
    }
    

    This returns the value of _lastHit, and if it was 0 swaps it with 0 (i.e. does nothing other than read the value atomically).

    Simply reading is no good - at a minimum because the variable isn't marked as volatile, so subsequent reads may just reuse a cached value. Combining volatile & Interlocked would possibly work here (I'm not entirely sure, but I think an interlocked write cannot be seen in an inconsistent state even by another core doing a non-interlocked read). But if you do this you'll get a warning and a code smell for combining two different techniques.

    Option 2: use a lock. Less desirable in this situation because the Interlocked approach is more performant in this case. But you can store the correct type, and it's marginally clearer:

    DateTime _lastHit;
    object _lock = new object();
    
    void Touch()
    {
        lock (_lock)
            _lastHit = DateTime.Now;
    }
    

    You must use a lock to read this value too! Incidentally, besides mutual exclusion a lock also ensures that cached values can't be seen and reads/writes can't be reordered.

    Non-option: do nothing (just write the value), whether you mark it as volatile or not. This is wrong - even if you never read the value, your writes on a 32 bit machine may interleave in such an unlucky way that you get a corrupted value:

    Thread1: writes dword 1 of value 1
    Thread2: writes dword 1 of value 2
    Thread2: writes dword 2 of value 2
    Thread1: writes dword 2 of value 1
    
    Result: dword 1 is for value 2, while dword 2 is for value 1
    

提交回复
热议问题