【问题标题】:Is it possible to conditionally update a nullable long without locking?是否可以在不锁定的情况下有条件地更新可为空的 long?
【发布时间】:2018-11-29 09:58:47
【问题描述】:

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

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

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

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

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

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

【问题讨论】:

  • 你不能使用Interlocked,因为它不支持Nullable&lt;long&gt;
  • 你可以使用 int64 而不是 long。
  • @GabrielPereira,Int64long
  • 互锁操作支持的最大大小是 64 位(至少在 .NET 中公开)。 Nullable&lt;T&gt; 至少是 sizeof(bool) + sizeof(T),所以对于 long,你肯定会超过。
  • 我在您发布的代码中看不到任何锁定。

标签: c# multithreading locking interlocked


【解决方案1】:

问题结束:

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

对于不可为空的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 检查。您可能还希望/需要在循环内重新计算 newValue

Interlocked.Read 为您获取初始值。 Interlocked.CompareExchange 执行“如果该值仍然与我上次读取该值时的值相同,则提交我的更改”,并且如果值已更改,则还获取新的当前值。

需要考虑的其他事项 - 您真的需要这个“开始时间”值还是可以用一个简单的递增整数代替它?那将是Interlocked.Increment,没有循环也没有锁定,因此如果您可以适当地修改代码的其他部分,那么如果这里的写争用仍然很高,可能值得考虑。

【讨论】:

  • 谢谢!为什么我们使用while 而不是if
  • 您不必这样做,但它涵盖了“我在评估我的状况时使用的current 的值不是现在的值”。很多时候,如果该新值满足您的条件,您仍希望将您的“更好”newValue 放在它的位置。所以我们循环直到条件完全不满足。 (newValue 满足条件并产生无限循环的不同条件是可能的,这就是为什么我也总是包含额外的break 位。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多