2009-06-10 7 views
1

Я получаю исключение NullReferenceException при запуске моего многопоточного приложения, но только когда я запускаю режим Release вне отладчика. Трассировка стека регистрируется и всегда указывает на тот же вызов функции. Я поставил несколько операторов протоколирования в функцию, чтобы попытаться определить, как далеко она будет получена, и каждый оператор будет зарегистрирован, в том числе один в последней строке функции. Что интересно, когда происходит NullReferenceException, оператор после вызова функции не нужно регистрироваться:NullReferenceException при возврате функции

// ... 
    logger.Log("one"); // logged 
    Update(false); 
    logger.Log("eleven"); // not logged when exception occurs 
} 

private void Update(bool condition) 
{ 
    logger.Log("one"); // logged 
    // ... 
    logger.Log("ten"); // logged, even when exception occurs 
} 

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

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

foreach (ClassItem item in classItemCollection) 

Я проверил через лесозаготовки, что «classItemCollection» не является нулевым, и я также попытался изменить foreach на a for, если IEnumerator делает что-то смешное, но исключение происходит в одной строке.

Любые идеи о том, как исследовать это дальше?

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

+0

Может содержать код функции здесь? Может быть, легче определить, что происходит. –

+0

> «Возможно ли, что стек поврежден ...?»

+1

re "eleven" не регистрируется - возможно, это просто не краснеет? –

ответ

2

Я нашел свою нулевую ссылку. Как предположил Фредрик и Микатан, я не предоставил достаточной информации для сообщества, чтобы найти решение, поэтому я решил, что должен опубликовать то, что нашел, чтобы положить это на отдых.

Это представление о том, что происходит:

ISomething something = null; 

//... 

// the Add method returns a strong reference to an ISomething 
// that it creates. m_object holds a weak reference, so when 
// "this" no longer has a strong reference, the ISomething can 
// be garbage collected. 
something = m_object.Add(index); 

// the Update method looks at the ISomethings held by m_object. 
// it obtains strong references to any that have been added, 
// and puts them in m_collection; 
Update(false); 

// m_collection should hold the strong reference created by 
// the Update method. 
// the null reference exception occurred here 
something = m_collection[ index ]; 

return something; 

Проблема оказалась моим использование «что-то» переменной в качестве временной сильной ссылки, пока метод Update не получил постоянной. Компилятор в режиме Release оптимизирует «something = m_object.Add();» потому что «что-то» не используется до тех пор, пока оно не будет назначено снова. Это позволило удалить ISomething, потому что он больше не существовал в m_collection, когда я пытался получить к нему доступ.

Все, что мне нужно было сделать, это обеспечить, чтобы я держал сильную ссылку до тех пор, пока не позвонил «Обновить».

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

2

Тот факт, что он записывает «десять» заставит меня смотреть в первую очередь на:

  • является logger когда-либо назначен ... это, возможно, становится нулевым каким-то образом
  • это ошибка внутри Log самого

Трудно сказать без достаточного контекста для этого - но именно так я бы его исследовал. Вы также можете добавить простой тест null; как нахальный подход, вы можете переименовать Log метод к чему-то еще, и добавить метод расширения:

[Conditional("TRACE")] 
public static void Log(this YourLoggerType logger, string message) { 
    if(logger==null) { 
     throw new ArgumentNullException("logger", 
      "logger was null, logging " + message); 
    } else { 
     try { 
      logger.LogCore(message); // the old method 
     } catch (Exception ex) { 
      throw new InvalidOperationException(
       "logger failed, logging " + message, ex); 
     } 
    } 
} 

Ваш существующий код должен вызывать новый Log метод расширения, и исключение будет ясно, где именно barfed , Возможно, измените его назад, если он был установлен ... или, может быть, оставит его.

+0

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

0

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

Редактировать: Можете ли вы разместить дополнительную информацию о типах ClassItem и classItemCollection?

Другая возможность заключается в том, что ClassItem является типом значений, а classItemCollection является общей коллекцией, и как-то нуль добавляется в коллекцию.Следующие броски a NullReferenceException:

 ArrayList list=new ArrayList(); 

     list.Add(1); 
     list.Add(2); 
     list.Add(null); 
     list.Add(4); 

     foreach (int i in list) 
     { 
      System.Diagnostics.Debug.WriteLine(i); 
     } 

Данная проблема может быть решена с помощью int? i или Object i в foreach или с использованием универсального контейнера.

+0

classItemCollection является локальным для функции. –

0

Согласен с Fredrik - требуется более подробная информация. Одно место, которое, возможно, начнет искать: вы упоминаете многопоточное приложение и ошибку, происходящую в выпуске, но не отлаживаемую. Возможно, вы столкнулись с проблемой синхронизации, когда несколько потоков обращаются к тем же ссылкам на объекты.

Несмотря на это, я бы, вероятно, также положить:

Debug.Assert(classItemCollection != null); 

прямо перед итерации цикла. Это не поможет вам в режиме выпуска, но это может помочь вам уловить проблему, если (когда?) Это происходит в Debug.

+0

Я положил «if (classItemCollection! = Null)» вокруг foreach, и исключение все еще происходит. Для этой коллекции действительно невозможно. Он может быть пустым, но не нулевым. –

+0

Если коллекция не является нулевой, я не уверен, почему вы получите NRE на этой строке кода. Мне любопытно, в чем проблема ... (плохо ли, если вы хотите, чтобы вы отлаживали такие вещи?) – micahtan

0

Я искал бы код, который устанавливает логгер или один из его иждивенцев в значение null. Существуют ли свойства logger, которые при установке в null могут вызвать это? Режим выпуска иногда ускоряет выполнение приложения, которое может выявлять проблемы синхронизации, которые маскируются снижением производительности режима отладки и/или отладчиком.

0

Тот факт, что «одиннадцать» не регистрируется, заставляет меня поверить, что регистратор установлен на нуль перед тем, как этот вызов будет выполнен. Можете ли вы обернуть его в try/catch и посмотреть, попадает ли он в ловушку блока? Возможно, вы можете вставить MessageBox.Show или записать что-то в известный файл, когда это произойдет.

Смежные вопросы