В чем преимущество предоставления полей объекта локальным переменным? - PullRequest
3 голосов
/ 10 января 2010

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

Я скачал исходный код .NET , чтобы более внимательно взглянуть на биты (с комментариями разработчиков, то, что Reflector не дает нам :-P)

Во всяком случае, во многих классах .NET я встречал следующий шаблон:

class SomeType
{
  // ...
  SomeClass m_Field;
  // ...
  SomeClass SomeMethod()
  { 
    SomeClass localField = m_Field;
    if (localField == null)
    {
      localField = new SomeClass(); 
      m_Field = localField;
    } 
    return localField; 
  }
}

Это заставило меня задуматься, в чем преимущество использования такого шаблона?

Насколько я знаю, приведенная выше схема хуже по производительности, чем приведенная ниже:

class SomeType
{
  // ...
  SomeClass m_Field;
  // ...
  SomeClass SomeMethod()
  { 
    if (m_Field == null)
    {
      m_Field = new SomeClass(); 
    } 
    return m_Field; 
  }
}

Или я что-то здесь упускаю?

Ответы [ 4 ]

2 голосов
/ 10 января 2010

Такой подход может помочь защитить вас от ситуаций, когда вы вызываете SomeMethod из одного потока, но после того, как вы проверили m_Field на null, управление передается другому потоку, который устанавливает его в null. Затем элемент управления возвращается в первый поток, но он все еще думает, что m_Field! = Null, что, вероятно, приведет к исключению NullReferenceException

Насколько я помню, есть пара слов об этом в "CLR via C #" Рихтера в главе о событиях.

2 голосов
/ 10 января 2010

Во многих случаях разница носит чисто эстетический и субъективный характер, но на ум приходят три причины думать об одном против другого:

  1. Потоковая безопасность: Алгоритм без блокировки может беспокоиться об этом, но если вся синхронизация выполняется с помощью блокировок, это не должно быть проблемой.

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

  3. Исключительная безопасность: Зачастую вам нужно соблюдать осторожность, чтобы вносить промежуточные изменения в локальные объекты, а затем публиковать результаты в полях только после завершения операций, не вызывая исключения. Это действует как механизм транзакций, так как никто не увидит объект только с половиной установленных полей.

0 голосов
/ 10 января 2010

MS .NET JIT делает регистр выделения . В таком простом случае временная переменная должна храниться в регистре, а не в стеке. Сгенерированный байт-код x86 должен затем работать быстрее, чем если бы элемент класса читался дважды (один раз, чтобы проверить на нулевое значение, один раз, чтобы возвратить) для ненулевого регистра, и быстрее также и для нулевого регистра.

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

Но если используется переменная local , компилятор предполагает, что ему не нужно записывать изменения в локальную переменную (или даже сохранять ее в стеке), потому что доступ к локальным переменным другого потока не является чем-то, что C # позволяет.

0 голосов
/ 10 января 2010

Это может быть просто подсказка компилятору, что поле должно быть считано в регистр, а не повторно обращено к нему в памяти. Есть небольшая причина, почему первая версия должна быть на хуже по производительности, чем вторая, которую вы перечислили. Это в значительной степени тот же самый код, который компилятор все равно сгенерирует. Считайте поле объекта в регистр, проверьте, является ли оно пустым, измените его по желанию, а затем запишите обратно в поле объекта в памяти.

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