什么(如果有的话)是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;
请注意延迟更新.
这是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
根本不能省略它仍然在一条指令中进行操作以使其相对于中断(以及单处理器上的其他线程)具有原子性.
奇怪的跳跃代码是在分支预测数据不可用的情况下的优化.毋庸置疑,尝试做得比许多年来由优秀的软件工程师和芯片制造商的慷慨帮助所做的更好的工作是一项艰巨的任务.
为什么不Interlocked.Exchange
适合你?
如果您需要确切的内存位置,然后你使用了错误的语言和平台的.NET抽象的内存管理,从而满足你不需要去想它交换.
如果你必须做这样的事情没有Interlocked.Exchange
,你可以写标记为一些代码unsafe
,做一个传统的基于指针的交换,你用C或C++可能,但你需要将它包装在一个合适的同步上下文,以便它是一个原子操作.
更新
您不需要求助于unsafe
代码以原子方式进行交换.您可以将代码包装在同步上下文中以使其成为原子.
lock (myLockObject) { var x = Interlocked.Exchange(a, b); Interlocked.Exchange(b, x); }
更新2
如果同步不是一个选项(如评论中所示),那么我相信你运气不好.当你追逐一些无法衡量的效率时,你可能想集中精力去其他地方.如果交换两个整数值是一个巨大的性能需求,你可能使用了错误的平台.
这是一个奇怪的想法.我不确切知道如何设置数据结构.但如果有可能你可以将你的两个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); }
当然,我很可能错误地实现了这一点.这个想法可能存在缺陷.这只是一个想法.
Interlocked.Exchange是你唯一可以做的事情:
var x = Interlocked.Exchange(a, b); Interlocked.Exchange(b, x);
你是正确的,这不是原子的,但是使用局部变量,只要两行执行,就可以保证值是一致的.您的其他选项是不安全的代码(用于使用指针),使用p/invoke到本机库,或重新设计以便不再需要它.
根据MSDN,Interlocked.Exchange 是原子的.
如果它不适合您,您可以使用C/C++ 在不安全的部分实现XCHG .