How do I atomically swap 2 ints in C#?

后端 未结 8 729
执念已碎
执念已碎 2020-11-29 06:50

What (if any) is the C# equivalent of the x86 asm xchg instruction?

With that command, which imo is a genuine exchange (unlike Interlocked.Exchange), I

相关标签:
8条回答
  • 2020-11-29 06:56

    Interlocked.Exchange is really the only thing you can do:

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

    You are correct that this will not be atomic, but with a local variable used you are guaranteed that the values are consistent as long as both lines execute. Your other options are unsafe code (for using pointers), using p/invoke to a native library, or redesigning so that it's no longer required.

    0 讨论(0)
  • 2020-11-29 07:00

    According to MSDN, Interlocked.Exchange is atomic.

    If it is not suitable for you, you could implement XCHG in an unsafe section using C/C++.

    0 讨论(0)
  • 2020-11-29 07:03

    Outside of Interlocked.Exchange, I'm assuming the XCHG command is probably an implementation of the XOR Swap, so you could write your own.

    C syntax: (from wikipedia link)

     void xorSwap (int *x, int *y) {
         if (x != y) {
             *x ^= *y;
             *y ^= *x;
             *x ^= *y;
         }
     }
    

    Edit this is not atomic, you would have to synchronize it yourself

    0 讨论(0)
  • 2020-11-29 07:05

    Interlocked.Exchange is best way to swap two int values in a thread safe manner in c#.

    Use this class , even you have multiprocessor computer and you never know in which processor your thread is going to run.

    0 讨论(0)
  • 2020-11-29 07:13

    Here's kind of a weird idea. I don't know exactly how you have your data structure set up. But if it's possible you could store your two int values in a long, then I think you could swap them atomically.

    For example, let's say you wrapped your two values in the following manner:

    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);
        }
    }
    

    Then it seems you could add the following Swap method to result in a swapped pair "atomically" (actually, I don't know if it's fair to really say that the following is atomic; it's more like it produces the same result as an atomic swap).

    /// <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);
    }
    

    It is highly possible I've implemented this incorrectly, of course. And there could be a flaw in this idea. It's just an idea.

    0 讨论(0)
  • 2020-11-29 07:14

    This is the likely implementation for Interlocked.Exchange() in the CLR, copied from the SSCLI20 source:

    Note that UP in the function name means UniProcessor. This is not atomic on SMP / multi-core systems. This implementation will only be used by CLR on single-core systems.

    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
    

    It is superior to using XCHG because this code works without taking a bus lock. xchg has an implicit lock prefix, so unlike xadd or cmpxchg it simply can't be omitted for single-core systems to still do the operation in one instruction to make it atomic with respect to interrupts (and thus other threads on uniprocessor).

    The odd looking jumping code is an optimization in case branch prediction data is not available. Needless to say perhaps, trying to do a better job than what has been mulled over for many years by very good software engineers with generous helpings from the chip manufacturers is a tall task.

    0 讨论(0)
提交回复
热议问题