Требуется ли блокировка доступа к требуемому bool или это Overkill

У меня есть класс, который разработан в основном как класс POCO, при этом различные Threads и Tasks могут читать его значения, а только другие только изредка обновляют эти значения. Это, кажется, идеальный сценарий для ReaderWriterLockSlim.

Вопрос в классе, если свойство, которое должно быть потокобезопасным, если свойство является bool, является ли это излишним? что произойдет, если это int? DateTime?

public class MyClass
{
  private bool _theValue = false;
  private ReaderWriterLockSlim _theValueLock = new ReaderWriterLockSlim();

  public bool TheValue
  {
    get
    {
      bool returnVal = false;
      try
      {
        _theValueLock.EnterReadLock();
        returnVal = _theValue;
      }
      finally
      { _theValueLock.ExitReadLock(); }
      return returnVal;
    }
    set
    {
      try
      {
        _theValueLock.EnterWriteLock();
        _theValue = value;
      }
      finally
      { _theValueLock.ExitWriteLock(); }
    }
  }
}

Все это переполнение кода и простой...

public bool TheValue { get; set; }

... было бы достаточно? Потому что Type is bool, безопасно ли это? если да, когда это становится небезопасным? байт? Int? DateTime?

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

Заключение
Спасибо всем за их ответ, все были ценны. Моя главная забота заключалась в атомарности писем/чтений (т.е. Беспокоились о расщеплении). Для платформы .NET, если рассматриваемая переменная представляет собой встроенный тип значения, который меньше 4 байтов, тогда чтение и запись являются атомарными (например, short и int являются точными, длинными и двойными не являются).

Ответ 1

У вас есть несколько вариантов.

  • Ничего не делать.
  • Сделать класс неизменным.
  • Используйте простой старый lock.
  • Отметьте поля как volatile.
  • Используйте набор методов Interlocked.

Так как чтение и запись в bool гарантированно являются атомарными, вам может не понадобиться ничего делать. Это будет сильно зависеть от характера того, как будет использоваться ваш класс. Атомарность не соответствует безопасности потоков. Это только один аспект.

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

Следующим моим предпочтением в списке является простой старый lock. Накладные расходы на приобретение и выпуск очень минимальны. На самом деле, я думаю, вы обнаружите, что lock будет выбивать ReaderWriterLockSlim в большинстве ситуаций, особенно если все, что вы делаете, это чтение/запись переменной. Мои собственные тесты показывают, что накладные расходы RWLS примерно в 5 раз медленнее, чем lock. Поэтому, если операции чтения необычно длинны и что они значительно превышают количество операций записи, тогда RWLS не поможет.

Если вы беспокоитесь о накладных расходах, то обязательно отметьте поля как volatile. Просто помните, что volatile - не волшебная пуля, которая решает все проблемы concurrency. Он не предназначен для замены lock.

Ответ 2

В зависимости от того, как это используется, вам может потребоваться отметить boolean volatile. Для этого вам потребуется поле поддержки.

Вам не нужно обрабатывать это с помощью ReaderWriterLockSlim, как вы сейчас делаете, так как оно меньше 32 бит (при условии, что вы используете AutoLayout, для получения дополнительной информации см. этот пост или, что наиболее подробно, раздел под названием "Атоматичность памяти" в спецификации ECMA 335). Если вы используете тип, больший, чем этот, тогда потребуется некоторая форма синхронизации.

Я бы порекомендовал:

public class MyClass
{
    private volatile bool _theValue = false;
    public bool TheValue 
    {
        get { return _theValue; } 
        set { _theValue = value; } 
    }
 }

Ответ 3

Ни один тип не является действительно безопасным! Точнее, спецификации С# гарантируют вам, что чтение или назначение типов структуры менее 4 байтов или ссылок являются атомарными. если ваша операционная система - 64 бита, CLR делает немного лучше, гарантируя одно и то же для структур менее 8 байтов.

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

Даже что-то простое:

myBool = !myBool

может получить неожиданный результат, если конкурирующий поток изменит значение myBool.

Рекомендуется использовать блокировки, если вы хотите убедиться, что чего-то подобного не происходит. Использование ключевого слова volatile сильно обескуражено, если вы точно не знаете, что делаете. Проверьте эти blog posts для дополнительной информации.

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