Одно из возможных исправлений: чтобы вставить барьер памяти как в ThreadA, так и в ThreadB... JIT CLR вставит инструкцию «lock or» вместо барьера памяти. Заблокированная инструкция x86 имеет побочный эффект очистки буфера хранилища
и действительно, глядя на выходные данные инструментов dotnet на godbolt.org, я вижу что System.Threading.Thread.MemoryBarrier() компилируется (предположительно, обрабатывается) до следующего значения:
Код: Выделить всё
lock
or dword ptr [rsp], 0
ret
Это кажется слегка удивительным... Intel предоставила mfence инструкция, которая кажется идеальной для этой цели, и более старый метод платформы dotnet Interlocked.SpeculationBarrier задокументирован для создания mfence под x86 и amd64 (как и старый Thread.VolatileRead и запись методы с тех пор они устарели). У меня нет подходящих инструментов для просмотра сгенерированной сборки MemoryBarrier() для других архитектур, но документация по модели памяти предполагает, что ARM64 получает инструкцию dmb, которая представляет собой полный барьер памяти и, следовательно, предположительно эквивалентна to mfence.
От BeeOnRope есть интересный ответ на вопрос: имеет ли lock xchg такое же поведение, как mfence? это говорит о том, что mfence при некоторых обстоятельствах предлагает более сильные гарантии, чем lock, и я не могу высказать свое мнение по этому поводу, но даже если бы эти две инструкции были абсолютно эквивалентны: при прочих равных условиях я бы выбрали mfence как более очевидный вариант. Вероятно, инженеры-компиляторы Microsoft знают лучше.
Тогда возникает вопрос: почему lock или вместо mfence?
Подробнее здесь: https://stackoverflow.com/questions/792 ... -of-mfence