Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/hibernate/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么不存在接受双精度作为参数的Interlocated.Add重载?_C#_.net_Vb.net_Multithreading_Atomic - Fatal编程技术网

C# 为什么不存在接受双精度作为参数的Interlocated.Add重载?

C# 为什么不存在接受双精度作为参数的Interlocated.Add重载?,c#,.net,vb.net,multithreading,atomic,C#,.net,Vb.net,Multithreading,Atomic,我完全理解Threading.Interlocked类提供的原子性;不过,我不明白为什么Add函数只提供两个重载:一个用于整数,另一个用于long。为什么不使用double或任何其他数字类型呢 显然,更改Double的预期方法是CompareExchange;我猜这是因为修改Double比修改整数更复杂。我仍然不清楚为什么,如果CompareExchange和Add都能接受整数,它们就不能同时接受双精度。我怀疑有两个原因 Net目标处理器仅支持整数类型的联锁增量。我相信这是x86上的锁前缀,其他

我完全理解Threading.Interlocked类提供的原子性;不过,我不明白为什么Add函数只提供两个重载:一个用于整数,另一个用于long。为什么不使用double或任何其他数字类型呢


显然,更改Double的预期方法是CompareExchange;我猜这是因为修改Double比修改整数更复杂。我仍然不清楚为什么,如果CompareExchange和Add都能接受整数,它们就不能同时接受双精度。

我怀疑有两个原因

  • Net目标处理器仅支持整数类型的联锁增量。我相信这是x86上的锁前缀,其他处理器可能也有类似的指令
  • 如果一个浮点数足够大的话,加上一个可以得到相同的数,所以我不确定你是否可以称之为增量。在这种情况下,框架设计者可能正试图避免非直觉行为

  • Interlocated类围绕Windows API Interlocated**函数展开

    反过来,它们围绕本机处理器API,使用x86的锁指令前缀。它仅支持在以下指令前加前缀:

    BT、BTS、BTR、BTC、XCHG、XADD、ADD、OR、ADC、SBB和、SUB、XOR、NOT、NEG、INC、DEC

    您将注意到,这些方法反过来几乎映射到互锁方法。不幸的是,这里不支持非整数类型的ADD函数。64位平台支持64位长的Add


    这是一篇很棒的文章。

    正如Reed Copsey所说,联锁操作(通过Windows API函数)映射到x86/x64处理器直接支持的指令。假设其中一个函数是XCHG,您可以执行原子XCHG操作,而不必真正关心目标位置的位代表什么。换句话说,代码可以“假装”正在交换的64位浮点数实际上是一个64位整数,而XCHG指令不会知道其中的差别。因此,.Net可以通过“假装”它们分别是整数和长整数,为浮点和双精度提供互锁的.Exchange函数

    但是,所有其他操作实际上都对目标的各个位进行操作,因此除非这些值实际表示整数(在某些情况下是位数组),否则它们不会工作。

    其他操作已经解决了“为什么?”。但是,使用
    compareeexchange
    原语可以轻松地滚动您自己的
    添加(ref double,double)

    public static double Add(ref double location1, double value)
    {
        double newCurrentValue = location1; // non-volatile read, so may be stale
        while (true)
        {
            double currentValue = newCurrentValue;
            double newValue = currentValue + value;
            newCurrentValue = Interlocked.CompareExchange(ref location1, newValue, currentValue);
            if (newCurrentValue == currentValue)
                return newValue;
        }
    }
    
    CompareExchange
    location1
    的值设置为
    newValue
    ,前提是当前值等于
    currentValue
    。因为它是以原子的、线程安全的方式实现的,所以我们可以单独依赖它,而不用使用锁

    为什么
    while(true)
    循环?这样的循环在实现优化并发算法时是标准的<如果当前值与当前值不同,则代码>比较交换不会更改
    位置1
    。我将
    currentValue
    初始化为
    location1
    ——执行非易失性读取(这可能会过时,但不会改变正确性,因为
    compareeexchange
    将检查该值)。如果当前值(仍然)是我们从
    位置
    读取的值,
    比较交换
    会将该值更改为
    新值
    。如果没有,我们必须使用新的当前值重试
    CompareExchange
    ,该值由
    CompareExchange
    返回


    如果该值被另一个线程更改,直到下一次
    CompareExchange
    再次执行时,它将再次失败,需要再次重试-理论上,这可能永远持续下去,因此会出现循环。除非您不断更改来自多个线程的值,
    CompareExchange
    很可能只被调用一次,如果当前值仍然是对
    location1
    的非易失性读取产生的值,或者是两次,如果它不同。

    关于您的第二点:是的,但我要问的是Interlocked.Add,不联锁。增量。你能再解释一下它是如何工作的吗?你为什么循环?“CompareExchange的作用是什么?”Mzn补充道。希望这能解释。这是一个很好的答案!至少现在我想我知道乐观并发意味着什么:“在编写并发代码时假设事情最终会顺利进行。”对于我们的while(true),这意味着我们不会永远循环(我们相当乐观)。非常感谢@MZN悲观并发:让我们锁定多个操作,因为它可能会出现多个步骤出错。可靠,并且是并发代码的可靠瓶颈。乐观并发:让我们使用
    CompareExchange
    进行一次无锁的尝试,因为大多数情况下,它只能与多个线程一起工作,如果不能,我们将重试。这允许更多的并发性,并且几乎总是更快。编程不容易,有些事情没有锁就无法完成。@JamPickle
    newCurrentValue=location1
    不是线程安全的,因为它不插入内存屏障。由于.Net 4.5可以这样做,但读取内存屏障会导致性能损失,因此对于当前值为
    0
    的情况,您将有一个用于易失性读取的内存屏障和一个用于
    compareeexchange
    的内存屏障。但是,易失性读取应该比
    compareeexchange
    快,但不确定.Net,因此对于
    0
    以外的当前值,使用易失性读取会更快。链接不再处于活动状态。这是一份来自Internet存档的备份