Когда процессор записывает в основную память при отсутствии энергозависимого механизма? - PullRequest
1 голос
/ 15 апреля 2020

Рассмотрим следующий код C# в многоядерной / многопроцессорной среде, работающей на x64 или ARM:

public sealed class Trio
{
    public long A;
    public long B;
    public long C;
}

public static class MP
{
    private static readonly object locker = new object();
    private static readonly Trio Data = new Trio();

    public static Trio ReadCopy()
    {
        lock (locker)
        {
            return new Trio { A = Data.A, B = Data.B, C = Data.C };
        }
    }

    public static void Set(long a, long b, long c)
    {
        lock (locker)
        {
            Data.A = a;
            Data.B = b;
            Data.C = c;
        }
    }
}

Очевидно, что синхронизация потоков явно выполняется.

Однако я есть вопрос, основанный на следующих наблюдениях, согласно моему пониманию:

  1. Оператор lock гарантирует, что а) только один поток может получить доступ к Data и б) поля в Data никогда не будут быть "порванным".
  2. Lock обеспечивает барьер памяти, который, насколько я вижу, не окажет заметного эффекта в этих двух контекстах.
  3. Поскольку поля не отмечены volatile и поскольку нет операций Volatile.Read() и Volatile.Write(), три поля будут записаны в кэш, а не напрямую в основную память.
  4. Единственный способ записи непосредственно в основную память - через один из вышеупомянутых «энергозависимых» механизмов, так как они используют операции ref и отключают оптимизацию, что приводит к чтению / записи в основной памяти.
  5. Глядя на код, процессор при некотором или неизвестно мне, запишите эти поля в основную память.
  6. Я не понимаю, почему несколько потоков гарантированно увидят последнюю версию трех полей, особенно в слабо упорядоченной архитектуре памяти, такой как ARM.

Мой вопрос: как я могу быть уверен, что вызов ReadCopy() после вызова Set() увидит последние значения для трех полей? Вызывающий поток может находиться на другом ядре и иметь свои собственные кэшированные копии Data.

Очевидно, что существуют «изменчивые» механизмы. Пример обычно вращается вокруг доступа к незаблокированным сегментам памяти. Но что из примера здесь? Я никогда не видел код, который использует lock , а использует изменчивый механизм.

1 Ответ

1 голос
/ 15 апреля 2020

Цитата из статьи C# - Модель памяти C# в теории и на практике от Игоря Островского:

Когда исполняется заблокированный блок кода, он гарантирован чтобы увидеть все записи из блоков, которые предшествуют блоку в последовательном порядке блокировки. Кроме того, гарантированно не будет видно ни одной записи из блоков, которые следуют за ней в последовательном порядке блокировки.

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

Я думаю, что это достаточно подробно отвечает на ваш вопрос!

Также есть часть 2: C# - Модель памяти C# в теории и практике, часть 2

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...