Написав ответ на вопрос another question, появилось несколько интересных вещей, и теперь я не могу понять, как Interlocked.Increment(ref long value)
работает на 32-битных системах. Позволь мне объяснить.Атомное приращение 64-битной переменной в 32-битной среде
InterlockedIncrement64
Native теперь не доступен при компиляции для 32-битной среды, хорошо, это имеет смысл, потому что в .NET вы не можете выровнять память по мере необходимости, и она может быть вызвана из удался затем сбросили его.
В .NET мы можем назвать Interlocked.Increment()
со ссылкой на переменную 64-битной, мы все еще не имеют каких-либо ограничений относительно его выравнивания (например, в структуре, также, где мы можем использовать FieldOffset
и StructLayout
), но документация Безразлично Ограничение (AFAIK). Это волшебство, это работает!
Ханс Passant отметил, что Interlocked.Increment()
является специальный метод признан JIT компилятором, и он будет излучать вызов COMInterlocked::ExchangeAdd64(), который будет затем вызвать FastInterlockExchangeAddLong, который представляет собой макрос InterlockedExchangeAdd64, который разделяет те же ограничения из InterlockedIncrement64.
Теперь я озадачен.
Забудьте в течение одной секунды управляемой среды и вернитесь к родной. Почему InterlockedIncrement64
не может работать, но InterlockedExchangeAdd64
делает? InterlockedIncrement64
макрос, если встроенные функции не доступны и InterlockedExchangeAdd64
работы, то он может быть реализован как призыв к InterlockedExchangeAdd64
...
Давайте вернемся к управляемым: как атомный 64 бит прирост реализован на 32-битных системах? Я полагаю, что предложение «Эта функция является атомарной в отношении вызовов к другим взаимосвязанным функциям.« важен, но все же я не видел никакого кода (спасибо Хансу указать на более глубокую реализацию), чтобы сделать это. Давайте выберем InterlockedExchangedAdd64
реализацию из WinBase.h, когда встроенные функции не доступны:
FORCEINLINE
LONGLONG
InterlockedExchangeAdd64(
_Inout_ LONGLONG volatile *Addend,
_In_ LONGLONG Value
)
{
LONGLONG Old;
do {
Old = *Addend;
} while (InterlockedCompareExchange64(Addend,
Old + Value,
Old) != Old);
return Old;
}
Как это может быть атомарным для чтения/записи?
Кто сказал, что «InterlockedIncrement64» не может работать, но «InterlockedExchangeAdd64' делает»? Ваш первоначальный ответ был прав, говоря, что управляемый код не может напрямую вызывать собственные API Win32 и ожидать, что все будет работать. Ни один из них не собирается работать. Вы должны использовать управляемый помощник. Теперь реализация управляемого помощника - это собственный код, поэтому он вызывает функцию native. Поскольку макросы и внутренности решаются во время компиляции, то считается, что бит CLR. –
Да, но 32-разрядный JIT вызовет InterlockedExchangeAdd64, который имеет те же ограничения (в native), что и InterlockedIncrement64. Я не понял, как это можно сделать (из-за выравнивания памяти при вызове управляемого кода). Реализация на 32-битном языке использует InterlockedCompareExchange64, который ... hmmm .... может быть не атомарным (для записи результата обратно ...) –
* «Как он может быть атомарным для чтения/записи?» * Документация для подсказок 'InterlockedExchangeAdd64' по этой причине, говоря * «Эта функция генерирует полный барьер памяти (или забор), чтобы гарантировать, что операции с памятью выполняются по порядку». * Обратите внимание, что реализация, которую вы показываете выше, вызывает «InterlockedCompareExchange64». В 32-битных сборках это генерирует инструкцию 'CMPXCHG8B' с префиксом' LOCK'. Это гарантирует, что инструкция выполняется атомарно. Вы никогда не получаете запертое чтение без заблокированной записи, поэтому запись адресата является атомарной. –