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

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

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 обработку синхронизации для этого критического раздела? Или я ошибаюсь, думая, что одновременное чтение нуждается в синхронизации?

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

Ответ 3

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

Ответ 4

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

Ответ 5

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

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

Ответ 6

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

Ответ 7

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

Ответ 8

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

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

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

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

public int getPrice() {
  return price;
}

setPrice (v) устанавливает цену продукта на v и корректирует его с учетом НДС (программа должна иметь value += TAX; price = value, но здесь дело не в этом: -)

Если поток A записывает цену 100, а TAX (фиксированный) 1, цена продукта, наконец, будет установлена ​​на 101. Но что произойдет, если поток B прочитает цену через getPrice(), а поток A - point CRITIC? Цена, возвращенная B, пропустит TAX и будет ошибочной.

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

    lock(this)
    {
      price = value;
      price += TAX;
    }