当前位置:  开发笔记 > 编程语言 > 正文

如何在C#中自动交换2个整数?

如何解决《如何在C#中自动交换2个整数?》经验,为你挑选了5个好方法。

什么(如果有的话)是x86 asm xchg指令的C#等价物?

有了这个命令,哪个imo是一个真正的交换(不像Interlocked.Exchange),我可以简单地自动交换两个int,这就是我真正想做的事情.

更新:

示例代码基于我的建议.变量后缀"_V"被装饰为volatile:

// PART 3 - process links
// prepare the new Producer
address.ProducerNew.WorkMask_V = 0;
// copy the current LinkMask
address.ProducerNew.LinkMask_V = address.Producer.LinkMask_V;
// has another (any) thread indicated it dropped its message link from this thread?
if (this.routerEmptyMask[address.ID] != 0)
{
  // allow all other bits to remain on (i.e. turn off now defunct links)
  address.ProducerNew.LinkMask_V &= ~this.routerEmptyMask[address.ID];
  // reset
  this.routerEmptyMask[address.ID] = 0;
}
// PART 4 - swap
address.ProducerNew = Interlocked.Exchange(ref address.Producer, address.ProducerNew);
// PART 5 - lazily include the new links, make a working copy
workMask = address.Producer.LinkMask_V |= address.ProducerNew.WorkMask_V;

请注意延迟更新.



1> Hans Passant..:

这是CLR中Interlocked.Exchange()的可能实现,从SSCLI20源复制:

请注意,函数名称中的UP表示UniProcessor.这在SMP /多核系统上不是原子的.此实现仅供CLR在单核系统上使用.

FASTCALL_FUNC ExchangeUP,8
        _ASSERT_ALIGNED_4_X86 ecx
        mov     eax, [ecx]      ; attempted comparand
retry:
        cmpxchg [ecx], edx
        jne     retry1          ; predicted NOT taken
        retn
retry1:
        jmp     retry
FASTCALL_ENDFUNC ExchangeUP

它优于使用XCHG,因为此代码无需使用总线锁即可运行. xchg具有隐式lock前缀,因此与单核系统不同xadd或者cmpxchg根本不能省略它仍然在一条指令中进行操作以使其相对于中断(以及单处理器上的其他线程)具有原子性.

奇怪的跳跃代码是在分支预测数据不可用的情况下的优化.毋庸置疑,尝试做得比许多年来由优秀的软件工程师和芯片制造商的慷慨帮助所做的更好的工作是一项艰巨的任务.


确实.无疑.相当.+1

2> Jeff Yates..:

为什么不Interlocked.Exchange适合你?

如果您需要确切的内存位置,然后你使用了错误的语言和平台的.NET抽象的内存管理,从而满足你不需要去想它交换.

如果你必须做这样的事情没有Interlocked.Exchange,你可以写标记为一些代码unsafe,做一个传统的基于指针的交换,你用C或C++可能,但你需要将它包装在一个合适的同步上下文,以便它是一个原子操作.

更新
您不需要求助于unsafe代码以原子方式进行交换.您可以将代码包装在同步上下文中以使其成为原子.

lock (myLockObject)
{
  var x = Interlocked.Exchange(a, b);
  Interlocked.Exchange(b, x);
}

更新2
如果同步不是一个选项(如评论中所示),那么我相信你运气不好.当你追逐一些无法衡量的效率时,你可能想集中精力去其他地方.如果交换两个整数值是一个巨大的性能需求,你可能使用了错误的平台.


嗯,我这里有锁的问题 - 它不会使代码块内的语句原子本身 - 只有它的原子性来自其他地方的同一个锁 - 没有它的访问变量会打破原子只是像那样...

3> Dan Tao..:

这是一个奇怪的想法.我不确切知道如何设置数据结构.但如果有可能你可以将你的两个int值存储在a中long,那么我认为你可以原子地交换它们.

例如,假设您按以下方式包装了两个值:

class SwappablePair
{
    long m_pair;

    public SwappablePair(int x, int y)
    {
        m_pair = ((long)x << 32) | (uint)y;
    }

    /// 
    /// Reads the values of X and Y atomically.
    /// 
    public void GetValues(out int x, out int y)
    {
        long current = Interlocked.Read(ref m_pair);

        x = (int)(current >> 32);
        y = (int)(current & 0xffffffff);
    }

    /// 
    /// Sets the values of X and Y atomically.
    /// 
    public void SetValues(int x, int y)
    {
        // If you wanted, you could also take the return value here
        // and set two out int parameters to indicate what the previous
        // values were.
        Interlocked.Exchange(ref m_pair, ((long)x << 32) | (uint)y);
    }
}

然后你似乎可以添加以下Swap方法来导致交换对"原子地"(实际上,我不知道是否公平地说以下原子的;它更像它产生与原子交换相同的结果).

/// 
/// Swaps the values of X and Y atomically.
/// 
public void Swap()
{
    long orig, swapped;
    do
    {
        orig = Interlocked.Read(ref m_pair);
        swapped = orig << 32 | (uint)(orig >> 32);
    } while (Interlocked.CompareExchange(ref m_pair, swapped, orig) != orig);
}

当然,我很可能错误地实现了这一点.这个想法可能存在缺陷.这只是一个想法.


从ABA问题的解决方案中可以知道该技术,其中您将高32位的递增"版本"计数器和低32位的数据保持在一起,以检测ABA故障.请参阅Duffy的"Windows上的并发编程"第536页.我写了一个基于64位原子操作的托管无锁字典,完全是在这样的配对32位邻居上完成的.
@IanC:太棒了!通常情况下,我回顾一下两年前我发布给SO的答案并且畏缩不前; 但很高兴知道这个想法最终证明对你有用(尽管我确定你必须做出重大改变才能将它应用到你的场景中).

4> Philip Rieck..:

Interlocked.Exchange是你唯一可以做的事情:

  var x = Interlocked.Exchange(a, b);
  Interlocked.Exchange(b, x);

你是正确的,这不是原子的,但是使用局部变量,只要两行执行,就可以保证值是一致的.您的其他选项是不安全的代码(用于使用指针),使用p/invoke到本机库,或重新设计以便不再需要它.



5> Rob Vermeule..:

根据MSDN,Interlocked.Exchange 原子的.

如果它不适合您,您可以使用C/C++ 在不安全的部分实现XCHG .

推荐阅读
重庆制造漫画社
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有