Как я могу поменять местами 2 ints в С#? - программирование
Подтвердить что ты не робот

Как я могу поменять местами 2 ints в С#?

Что (если есть) является эквивалентом С# команды ASM "XCHG" .

С этой командой, которая imo является подлинным обменом (в отличие от Interlocked.Exchange), я мог бы просто атомарно поменять два ints, чего я действительно пытаюсь сделать.

Update:

Пример кода, основанный на моем предложении. Переменная суффикс "_V" декорирована как изменчивая:

// 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.

4b9b3361

Ответ 1

Вот какая-то странная идея. Я точно не знаю, как вы настроили свою структуру данных. Но если возможно, вы можете сохранить два значения 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, чтобы привести к замене пары "атомарно" (на самом деле, я не знаю, справедливо ли сказать, что следующее является атомарным, оно больше похоже на тот же результат, что и атомный своп).

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

Очень возможно, что я сделал это неправильно, конечно. И в этой идее может быть недостаток. Это просто идея.

Ответ 2

Это вероятная реализация для Interlocked.Exchange() в CLR, скопированная из источника SSCLI20:

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, потому что этот код работает без блокировки шины. Нечетно выглядящий прыгающий код - это оптимизация в случае, если данные предсказания ветвления недоступны. Излишне говорить, что, возможно, пытаясь сделать лучшую работу, чем то, что было многолетним на протяжении многих лет, очень хорошие инженеры-программисты с щедрой помощью от производителей чипов - высокая задача.

Ответ 3

Почему не подходит Interlocked.Exchange?

Если вам нужны точные ячейки памяти, которые нужно поменять местами, вы используете неправильный язык и платформу, так как .NET абстрагирует управление памятью, так что вам не нужно об этом думать.

Если вы должны сделать что-то подобное без Interlocked.Exchange, вы можете написать код, помеченный как unsafe и выполнять традиционный обмен на основе указателей, как вы могли бы на C или С++, но вам нужно будет обернуть его в подходящий контекст синхронизации, чтобы он был атомной операцией.

Обновление
Вам не нужно прибегать к коду unsafe для автоматической смены. Вы можете обернуть код в контексте синхронизации, чтобы сделать его атомарным.

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

Обновление 2
Если синхронизация не является вариантом (как указано в комментариях), то я считаю, что вам не повезло. Когда вы преследуете некоторую неизмеримую эффективность, вы можете сконцентрироваться в другом месте. Если замена двух целых значений является огромной производительностью, вы, вероятно, используете неправильную платформу.

Ответ 4

Interlocked.Exchange - это единственное, что вы можете сделать:

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

Вы правы, что это не будет атомарным, но с использованием локальной переменной вам гарантировано, что значения будут согласованы до тех пор, пока выполняются обе строки. Ваши другие варианты - это небезопасный код (для использования указателей), с использованием p/invoke в родной библиотеке или перепроектирование, чтобы оно больше не требовалось.

Ответ 5

Согласно MSDN, Interlocked.Exchange является атомарным.

Если это не подходит для вас, вы можете реализовать XCHG в небезопасном разделе, используя C/С++.

Ответ 6

Interlocked.Exchange - лучший способ поменять два значения int в потоковом безопасном режиме в С#.

Используйте этот класс, даже если у вас многопроцессорный компьютер, и вы никогда не знаете, в каком процессоре будет работать ваш поток.

Ответ 7

Вне Interlocked.Exchange, я предполагаю, что команда XCHG, вероятно, является реализацией XOR Swap, поэтому вы можете написать свой собственный.

Синтаксис C: (из ссылки по Википедии)

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

Изменить, это не является атомарным, вам придется синхронизировать его самостоятельно

Ответ 8

Думаю, я нашел лучшее решение. Это:

Interlocked.Exchange() Метод (ref T, T)

Все "новые" переменные могут быть установлены в классе (Of T) и заменены текущими переменными. Это позволяет осуществить атомный снимок, эффективно заменяя любое количество переменных.