@exvel

[.NET] Является ли получение/запись элемента массива по индексу атомарной операцией?

В различных коллекциях .NET индексаторы являются свойствами и понятно, что получение или установка элемента по индексу является не атомарной операцией в этом случае.
Мне интересно, есть ли в данном случае исключение для массивов?
Может ли такая операция иметь право на жизнь или же обращение через индексатор и установка назад может быть прервана переключением контекста?
int original = Interlocked.CompareExchange(ref array[2], 5, 3);
Ссылка на массив не меняется, изменяются только значения элементов в ячейках разными потоками.

Update:
Судя по всему сгенерированный IL-код обращается с переменной массива как с любой другой переменной:
IL_0017: ldloc.0      // 'array'
IL_0018: ldc.i4.2     
IL_0019: ldelema      [mscorlib]System.Int32
IL_001e: ldc.i4.5     
IL_001f: ldc.i4.3     
IL_0020: call         int32 [mscorlib]System.Threading.Interlocked::CompareExchange(int32&, int32, int32)
IL_0025: pop
Я склоняюсь все же к тому, что операция атомарная, но лучше узнать мнение знающего человека.
  • Вопрос задан
  • 293 просмотра
Пригласить эксперта
Ответы на вопрос 1
IL код, это только половина дела.
Важна версия VM и битности сборки, debug/release режим.
Используйте windbg, там можно посмотреть код метода который будет выполнен CPU.

Я не отвечаю на вопрос, но даю информацию для размышления.
Книга - "Голдштейн С. - Оптимизация приложений на платформе .NET - 2014".

Выдержки из книги:
"Запись 32-разрядного целого числа всегда выполняется атомарно. Это означает, что, если процессор записывает в ячейку памяти значение 0xdeadbeef, прежде инициализированную нулем, другой процессор никогда не получит частично измененное значение в этой ячейке."

"К сожалению, то же самое нельзя сказать о более объемных данных; например, даже на 64-разрядном процессоре операция записи 20 байт не является атомарной и не может быть атомарной."

"Всякий раз, когда для выполнения операции с областью памяти требуется выполнить несколько инструкций, немедленно возникает проблема синхронизации. Например, операция ++i (где i является переменной на стеке типа int) обычно транслируется в последовательность из трех машинных инструкций:

   
 mov еах, dword ptr [ebp-64] ; скопировать со стека в регистр
    inc еах ;                                        увеличить значение в регистре
    mov dword ptr [ebp-64], еах ; скопировать из регистра на стек


Каждая из этих инструкций выполняется атомарно, но без дополнительной синхронизации есть вероятность, что два процессора вы­полнят части этой последовательности инструкций конкурентно. Это состояние гонки "race condition".

Все семейства процессоров, на которых Windows может выполняться, поддерживают аппаратный примитив синхронизации с названием «Сравнить-и-Заменить» (Compare-And-Swap, CAS).

В процессорах Intel х86 этот примитив реализован в виде инструкции LOCK cmpxchg. В .NET Framework примитив CAS реализован в виде множества перегруженных методов Interlocked.CompareExchange.

// Код на С#:
    int n = . . .;
    if (Interlocked.CompareExchange(ref n, 1, 0) = 0) ( // заменить 0 на 1
      // ...выполнить некоторые операции
    }
    // инструкции на языке ассемблера х8б:
    mov еах, 0 ;
    mov edx, 1 ;
    lock cmpxchg dword ptr [ebp-64], edx ;
    test eax, eax ;
    i
    jnz not__taken
    // ... выполнить некоторые операции
    not_taken:

"
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы