Как атомарно поменять 2 дюйма в C #? - PullRequest
26 голосов
/ 04 октября 2010

Что (если есть) является эквивалентом C # инструкции x86 xchg?

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

Обновление:

Пример кода, основанный на моем предложении.Переменные с суффиксом "_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 update.

Ответы [ 8 ]

58 голосов
/ 04 октября 2010

Это вероятная реализация Interlocked.Exchange () в CLR, скопированная из источника 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, одноядерные системы просто не могут пропустить операцию в одной инструкции, чтобы сделать ее атомарной по отношению к прерываниям (итаким образом, другие потоки в однопроцессоре).

Необычно выглядящий код перехода является оптимизацией в случае, если данные предсказания ветвления недоступны.Само собой разумеется, что попытка сделать лучшую работу, чем то, о чем много лет говорили очень хорошие инженеры-программисты с щедрой помощью производителей микросхем, является сложной задачей.

24 голосов
/ 04 октября 2010

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

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

24 голосов
/ 04 октября 2010

Почему Interlocked.Exchange не подходит для вас?

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

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

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

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

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

8 голосов
/ 04 октября 2010

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

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

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

5 голосов
/ 04 октября 2010

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

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

1 голос
/ 04 октября 2010

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

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

0 голосов
/ 04 октября 2010

Я думаю, я только что нашел лучшее решение.Это:

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

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

0 голосов
/ 04 октября 2010

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...