Interlocked.Exchange() 具有依赖于读取锁定变量的自定义条件

Posted

技术标签:

【中文标题】Interlocked.Exchange() 具有依赖于读取锁定变量的自定义条件【英文标题】:Interlocked.Exchange() with custom condition that relies in reading the locked variable 【发布时间】:2018-08-18 21:46:59 【问题描述】:

原始代码:

var can = rateLock.WaitAsync();
if (can.IsCompletedSuccessfully) // for safety but do I need it?

    if (!increase)
    
        errorRate = (byte)(errorRate - Convert.ToByte(errorRate > 0));
    
    else
    
        errorRate++;
    
    if (errorRate > 50)
    
        TimerStop(true);
    
    rateLock.Release();

修改为:

if (increase)

    if (Interlocked.Increment(ref errorRate) > 50)
    
        TimerStop(true);
    

else

    Interlocked.Exchange(ref errorRate, (errorRate - Convert.ToInt32(errorRate > 0)));

问题: 如您所见,“递减”部分依赖于需要读取两次的相同变量值,然后执行布尔和减法运算,所有这些都在互锁上下文之外。

我真的很喜欢没有额外的 SempahoreSlim(异步环境)的可能性 - 有没有办法在自定义条件下进行递减工作,避免大量 IF(我需要保持 errorRate >=0)?

【问题讨论】:

好吧,我有一个丑陋的版本1,我想我会坚持下去。我希望这是可行的:hastebin.com/kukohorumi.cs 但我认为平等检查仍然可以搞砸我 【参考方案1】:

您可以使用CompareExchange pattern 进行更复杂的操作:

int initialErrorRate, int computedErrorRate;
do

  initialErrorRate = errorRate;
  if (increase)
  
    computedErrorRate = initialErrorRate + 1;
  
  else
  
    computedErrorRate = initialErrorRate - Convert.ToInt32(initialErrorRate > 0);
  

// set new error rate only when it was not changed inbetween, otherwise try again
while (initialErrorRate != Interlocked.CompareExchange(ref errorRate, computedErrorRate, initialErrorRate);

if (errorRate > 50)

  TimerStop(true);

【讨论】:

errorRate 超过 50 后需要归零怎么办?这就是我想出的:ghostbin.com/paste/dkdup 值更改本身是线程安全的,但是如果您像在链接中那样调用它两次,则错误率可能会增加或减少两次。做什么取决于调用 TimerStop 两次或更多次是否可以,如果不是,我怀疑没有锁是可能的。如果可以多次调用 TimerStop 并快速完成,则可以将其移入循环中。

以上是关于Interlocked.Exchange() 具有依赖于读取锁定变量的自定义条件的主要内容,如果未能解决你的问题,请参考以下文章

Interlocked.exchange 作为锁

Interlocked.Exchange<T> 比 Interlocked.CompareExchange<T> 慢吗?

引用分配是原子的,那么为啥需要 Interlocked.Exchange(ref Object, Object)?

如何为 C# 中的枚举类型应用 InterLocked.Exchange?

如何执行双重原子读取?

C#ThreadInterlocked 轻量级锁