是否可以在不锁定的情况下有条件地更新可为空的 long?

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

我有 long? startTime 来保留开始时间。

我有多个线程使用非 async 方法更新 startTime 并且在单独的线程中有另一个 async 方法只读取这个值。更新值的方法如下:

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

问题在于,写入器线程正在以非常非常高的频率更新 startTime,导致很多锁争用。那么,有没有什么方法可以在不锁定的情况下做到这一点(或者比普通锁更好的锁定机制)?

我正在考虑使用 Interlocked 但由于 if 子句,我认为这不正确?

编辑:如果需要,我可以将其更改为不可为空。

问题结尾:

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

对于不可为 null 的 long,您需要一个检查和更新循环(类似这样):

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

您可以使您的 while 子句条件任意复杂,它们实际上是您的 if 检查。您也可以 want/need 在循环内重新计算 newValue

Interlocked.Read给你取初始值。 Interlocked.CompareExchange 执行 "if the value is still the same as when I last read the value, commit my change" 并在值发生变化时获取新的当前值。

需要考虑的其他事项 - 您是否真的需要这个 "start time" 值,或者可以用一个简单的递增整数代替它吗?那将是 Interlocked.Increment,没有循环也没有锁定,所以如果写竞争仍然很高,如果您可以适当地修改代码的其他部分,那么可能值得考虑。