Являются ли назначение ссылок и чтение атомными операциями?

Я нашел несколько вопросов по этой же теме, но связанные с общими переменными (значениями и ссылочными типами) В принятом ответе этот вопрос говорится:

Раздел I, раздел 12.6.6 спецификации CLI гласит: "Соответствующий CLI должен гарантировать, что доступ для чтения и записи к правильно выровненным ячейкам памяти не больше, чем собственный размер слова, является атомарным, когда все обращения к записи в местоположение того же размера."

Ссылочные переменные (то есть классы) являются указателями, равными размеру родного слова, но у меня есть пара сомнений:

Гарантируются ли ссылки в правильно выровненных ячейках памяти?

Я не понимаю последнюю часть. Что это значит? "... когда все обращения к записи в местоположении имеют одинаковый размер".

Короче говоря, гарантирован ли obj2 быть допустимым на каждой итерации цикла в следующем коде?

class MyClass
{
    private OtherClass m_Object;

    void Thread1()
    {
        while(true)
        {
            OtherClass obj1 = new OtherClass();
            m_Object = obj1;
        }
    }

    void Thread2()
    {
        while (true)
        {
            OtherClass obj2 = m_Object;
            // Is obj2 guaranteed to be valid?
            obj2.Check();
        }
    }
}

Ответы

Ответ 1

Да, все гарантировано правильно выровнено, если вы не намеренно отклоняетесь от своего пути, чтобы скомпоновать вещи, что означает, что ссылочное задание/чтение гарантированно будет атомарным.

В разделе 12.6.6 спецификации CLI говорится следующее:

Без явного управления компоновкой (см. Раздел II (контрольный экземпляр Layout)) используется для изменения значения по умолчанию поведение, элементы данных не больше размер натурального слова (размер native int) должны быть надлежащим образом выровнены. Ссылки на объекты должны быть как будто они хранятся в размер родного слова.

В разделе 12.6.2 спецификации также содержатся дополнительные сведения о выравнивании и т.д.

Обратите внимание, что в вашем примере кода чтение в потоке 2 гарантировано будет атомарным, но не гарантируется, что он действительно увидит любые изменения, сделанные потоком 1: без соблюдения барьеров памяти или волатильности каждая нить может использовать свой собственный "вид", поля m_Object, не видя изменений, сделанных другими потоками.

Итак, например, поток 1 может создавать (атомарные) записи в свой собственный вид m_Object, но данные только когда-либо хранятся в кэше реестра или процессора и никогда не попадают в основную память. Аналогично, поток 2 также может делать (атомарные) чтения m_Object, но фактически считывать из кэша регистров или CPU, а не из основной памяти.