Search code examples
c#multithreadinglockinginterlocked

Is it possible to conditionally update a nullable long without locking?


I have long? startTime to hold start time.

I have multiple threads with non-async methods updating startTime and there's another async method in a separate thread that only ever read this value. The methods that update the value do so as below:

if (startTime == null || (newValue != 0 && newValue < startTime))
{
    startTime = newValue;
}

The issue is that, writer threads are updating startTime at a very very high frequency causing a lot of contention for the lock. So, is there any way I can do this without locking (or a better locking mechanism over a normal lock)?

I'm thinking of using Interlocked but because of the if clause, I think this is not correct?

EDIT: I can change it to a non-nullable if needed.


Solution

  • From end of question:

    EDIT: I can change it to a non-nullable if needed.

    For the non-nullable long, you need a check and update loop (something like this):

    var current = Interlocked.Read(ref startTime);
    while(current > newValue)
    {
      var other = Interlocked.CompareExchange(ref startTime, newValue, current);
      if(other==current) break;
      current = other;
    }
    

    You can make your while clause conditions arbitrarily complex, they're effectively your if check. You may also want/need to recompute newValue inside the loop as well.

    The Interlocked.Read obtains you the initial value. The Interlocked.CompareExchange does "if the value is still the same as when I last read the value, commit my change" and also obtains the new current value if the value had changed.

    Other things to consider - do you actually need this "start time" value or could a simple incrementing integer stand in it's place? That would be Interlocked.Increment with no looping and no locking, so may be worth considering if write contention is still high here, if you can modify the other parts of your code appropriately.