Подтвердить что ты не робот

Использование Interlocked.Exchange для обновления ссылок и Int32

Известно, что ссылка занимает 4 байта памяти в 32-битном процессоре и 8 байтов - в 64-битном процессоре. Таким образом, процессоры гарантируют, что одиночные чтения и записи в память с шагом натурального размера слова машины будут выполняться атомарно. С другой стороны, в классе Interlocked есть 2 метода:

public static int Exchange(
    ref int location1,
    int value
)

и

public static T Exchange<T>(
    ref T location1,
    T value
)
where T : class

Итак, возникает вопрос, почему Interlocked.Exchange необходим для Int32 и для ссылочных типов? Нельзя ли это сделать безопасно, просто используя простое назначение, потому что оно является атомарным?

4b9b3361

Ответ 1

Речь идет не только об атомарности. Это также касается видимости памяти. Переменная может храниться в основной памяти или в кеше ЦП. Если переменная хранится только в кэше ЦП, она не будет видна для потоков, работающих на разных ЦП. Рассмотрим следующий пример:

public class Test {
    private Int32 i = 5;

    public void ChangeUsingAssignment() {
        i = 10;
    }

    public void ChangeUsingInterlocked() {
        Interlocked.Exchange(ref i, 10);
    }

    public Int32 Read() {
        return Interlocked.CompareExchange(ref i, 0, 0);
    }
}

Теперь, если вы вызываете "ChangeUsingAssignment" в одном потоке и "Читать" в другом потоке, возвращаемое значение может быть 5, а не 10. Но если вы вызовете ChangeUsingInterlocked, "Read" вернет 10, как ожидалось.

 ----------         ------------         -------------------
|   CPU 1  |  -->  |   CACHE 1  |  -->  |                   |
 ----------         ------------        |                   |
                                        |        RAM        |
 ----------         ------------        |                   |
|   CPU 2  |  -->  |   CACHE 2  |  -->  |                   |
 ----------         ------------         -------------------

В приведенной выше диаграмме метод "ChangeUsingAssignement" может привести к тому, что значение 10 "застрянет" в CACHE 2 и не попадет в ОЗУ. Когда процессор 1 попытается прочитать его, он получит значение из ОЗУ, где он все равно 5. Использование блокировки вместо обычной записи гарантирует, что значение 10 будет полностью загружено в ОЗУ.

Ответ 2

Interlocked.Exchange имеет возвращаемое значение, позволяющее вам узнать, какое значение вы только что заменили. Это комбинация установки нового значения и получения старого значения, которое эти методы достигают.

Ответ 3

Обмен значением памяти и содержимым регистров CPU вообще не является атомарным. Вам обоим нужно читать и записывать местоположение памяти. Кроме того, методы Interlocked гарантируют, что операция является атомарной даже на многоядерных компьютерах, где каждое ядро ​​имеет свой собственный кеш и потенциально свой собственный вид основной памяти.

Ответ 4

Interlock.Exchange возвращает исходное значение при выполнении атомной операции. Все дело в том, чтобы обеспечить механизм блокировки. Таким образом, на самом деле это две операции: прочитайте исходное значение и установите новое значение. Эти два вместе не являются атомарными.