Как я могу поменять местами 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.
Ответы
Ответ 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) и заменены текущими переменными. Это позволяет осуществить атомный снимок, эффективно заменяя любое количество переменных.