什么(如果有的话)是x86 asm xchg
指令的C#等价物?
使用那个命令,imo是一个真正的交换(不像Interlocked.Exchange
),我可以简单地原子交换两个整数,这就是我真正想要做的。
更新
基于我的建议的示例代码。变量后缀“_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<IPC.Producer>(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;
请注意 lazy 更新。
答案 0 :(得分:58)
这是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 :(得分:24)
为什么Interlocked.Exchange
不适合你?
如果您需要交换确切的内存位置,那么您使用的是错误的语言和平台,因为.NET将内存管理抽象出去,因此您无需考虑它。
如果你必须在没有Interlocked.Exchange
的情况下做这样的事情,你可以编写一些标记为unsafe
的代码并像在C或C ++中一样进行传统的基于指针的交换,但是你需要将它包装在合适的同步上下文中,以便它是一个原子操作。
<强>更新强>
您不需要求助unsafe
代码以原子方式进行交换。您可以将代码包装在同步上下文中以使其成为原子。
lock (myLockObject)
{
var x = Interlocked.Exchange(a, b);
Interlocked.Exchange(b, x);
}
更新2
如果同步不是一个选项(如评论中所示),那么我相信你运气不好。当你追逐一些无法衡量的效率时,你可能想集中精力去其他地方。如果交换两个整数值是一个巨大的性能需求,你可能使用了错误的平台。
答案 2 :(得分:24)
这是一种奇怪的想法。我不确切知道如何设置数据结构。但是,如果有可能将int
中的两个long
值存储起来,那么我认为你可以原子地交换它们。
例如,假设您按以下方式包装了两个值:
class SwappablePair
{
long m_pair;
public SwappablePair(int x, int y)
{
m_pair = ((long)x << 32) | (uint)y;
}
/// <summary>
/// Reads the values of X and Y atomically.
/// </summary>
public void GetValues(out int x, out int y)
{
long current = Interlocked.Read(ref m_pair);
x = (int)(current >> 32);
y = (int)(current & 0xffffffff);
}
/// <summary>
/// Sets the values of X and Y atomically.
/// </summary>
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
方法来“原子地”导致交换对(实际上,我不知道是否公平地说下面的是 atomic;它更像是产生与原子交换相同的结果。)
/// <summary>
/// Swaps the values of X and Y atomically.
/// </summary>
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);
}
当然,我很可能错误地实现了这一点。这个想法可能存在缺陷。这只是一个想法。
答案 3 :(得分:8)
Interlocked.Exchange是你唯一可以做的事情:
var x = Interlocked.Exchange(a, b);
Interlocked.Exchange(b, x);
你是正确的,这不是原子的,但是使用局部变量,只要两行执行,就可以保证值是一致的。您的其他选项是不安全的代码(用于使用指针),使用p / invoke到本机库,或重新设计以便不再需要它。
答案 4 :(得分:5)
根据MSDN,Interlocked.Exchange 是原子。
如果它不适合您,您可以使用C / C ++在 unsafe 部分实现XCHG。
答案 5 :(得分:1)
Interlocked.Exchange是在c#中以线程安全方式交换两个int值的最佳方法。
使用此类,即使您拥有多处理器计算机,也不知道您的线程将在哪个处理器中运行。
答案 6 :(得分:0)
在Interlocked.Exchange之外,我假设XCHG命令可能是XOR Swap的一个实现,所以你可以编写自己的。
C语法:(来自维基百科链接)
void xorSwap (int *x, int *y) {
if (x != y) {
*x ^= *y;
*y ^= *x;
*x ^= *y;
}
}
编辑这不是原子的,你必须自己同步
答案 7 :(得分:0)
我想我刚刚找到了最佳解决方案。它是:
Interlocked.Exchange()方法(ref T,T)
所有“新”变量都可以在类(Of T)中设置,并与当前变量交换。这样可以实现原子快照,同时有效地交换任意数量的变量。