Почему неизменяемые объекты являются поточно-ориентированными?

class Unit {
    private readonly string name;
    private readonly double scale;

    public Unit(string name, double scale) {
        this.name = name;
        this.scale = scale,
    }

    public string Name { get { return name; } }
    public string Scale { get { return scale; } }

    private static Unit gram = new Unit("Gram", 1.0);

    public Unit Gram { get { return gram; } }
}

Несколько потоков имеют доступ к Unit.Gram . Почему нормально читать несколько потоков одновременно Unit.Gram.Title ?

Меня беспокоит то, что они ссылаются на одну и ту же область памяти. Один поток начинает читать эту память, так что, не заблокировано ли это? .NET обрабатывает синхронизацию для этого критического раздела внизу? Или я ошибаюсь, полагая, что одновременное чтение требует синхронизации?

33
задан randomguy 24 September 2010 в 22:36
поделиться

8 ответов

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

И это серьезная тема, но краткий ответ таков: да, два потока (и, что более важно, 2+ ЦП) могут читать (и/или записывать) один и тот же участок памяти одновременно.

И пока содержимое этой области памяти неизменно, все проблемы решены. Когда это может измениться, возникает целый ряд проблем, ключевое слово volatile и класс Interlocked — это некоторые из инструментов, которые мы используем для их решения.

13
ответ дан 27 November 2019 в 17:39
поделиться

Что делает объект не потокобезопасным? Объект не является потокобезопасным, если значение/состояние этого объекта может измениться, пока поток читает его. Обычно это происходит, если второй поток изменяет значение этого объекта, в то время как первый поток читает его.

Неизменный объект по определению не может изменить значение/состояние. Поскольку каждый раз, когда вы читаете неизменяемый объект, он имеет одно и то же значение/состояние, вы можете иметь любое количество потоков, читающих этот объект без каких-либо проблем.

59
ответ дан 27 November 2019 в 17:39
поделиться

Если объект неизменяем, его состояние никогда не изменится. Поэтому опасения по поводу устаревших данных исчезают. Чтение потока никогда не блокируется, поэтому это не проблема (взаимная блокировка)

4
ответ дан 27 November 2019 в 17:39
поделиться

Одновременное чтение не требует синхронизации в подавляющем большинстве случаев (исключения составляют операции ввода-вывода с отображением памяти, когда чтение с определенного адреса может вызывать побочные эффекты).

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

3
ответ дан 27 November 2019 в 17:39
поделиться

Чтение одной и той же области памяти может быть выполнено только за один цикл ЦП определенным потоком. Порядок чтения в этом случае не имеет значения, так как базовое значение не меняется. Следовательно, нет возможности несогласованности чтения, поэтому в этом случае нет необходимости в синхронизации на любом уровне.

2
ответ дан 27 November 2019 в 17:39
поделиться

Блоки управления памятью — это часть процессора, отвечающая за чтение памяти. Если у вас их несколько, однажды в голубой луне 2 из них могут попытаться прочитать одну и ту же ячейку памяти за одну и ту же дюжину наносекунд, но никаких проблем не возникнет, поскольку они получат один и тот же ответ.

1
ответ дан 27 November 2019 в 17:39
поделиться

Кроме исключений, таких как сопоставление памяти для драйверов, например, проблем нет. для двух потоков, читающих одновременно один и тот же адрес памяти. Проблема может возникнуть, когда один поток выполняет запись данных. В этом случае другим потокам может быть запрещено читать этот объект/данные.

Но проблема не в одновременности записей (на низшем электронном уровне они и так происходят одна за другой), проблема скорее в том, что объект/набор данных может потерять свою последовательность. Обычно используется раздел критика, чтобы изолировать некоторый код, который не может быть одновременно прочитан/записан другими потоками.

В Сети есть много примеров, но рассмотрим следующее: price является закрытым членом класса, скажем, Product, который также имеет 2 метода

public void setPrice(int value) {
  price = value;
  // -- point CRITIC --
  price += TAX;
}

public int getPrice() {
  return price;
}

setPrice(v) устанавливает цену продукта на v и скорректировать его с учетом НДС (в программе должно быть значение += НАЛОГ; цена = значение но здесь не в этом дело :-)

Если поток А пишет цену 100, а TAX равен (фиксированному) 1, цена продукта в конечном итоге будет установлена ​​на 101. Но что произойдет, если поток B прочитает цену через getPrice(), в то время как поток A находится в точке CRITIC? Цена, возвращенная В, не будет уплачена НАЛОГ и будет неправильной.

setPrice() должен находиться внутри критической секции (lock), чтобы предотвратить любой доступ к объекту во время установки цены

    lock(this)
    {
      price = value;
      price += TAX;
    }
1
ответ дан 27 November 2019 в 17:39
поделиться

Одновременное чтение не требует синхронизации. Поскольку синхронизация требуется только для модулей записи (или модулей чтения и хотя бы одного модуля записи), неизменяемые объекты не требуют синхронизации и, следовательно, являются потокобезопасными.

11
ответ дан 27 November 2019 в 17:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: