2009-06-10 7 views
1

Ich erhalte eine NullReferenceException, wenn meine Multithread-Anwendung ausgeführt wird, aber nur, wenn ich im Freigabe-Modus außerhalb des Debuggers ausgeführt werde. Der Stack-Trace wird protokolliert und verweist immer auf denselben Funktionsaufruf. Ich habe mehrere Logging-Anweisungen in die Funktion geschrieben, um zu versuchen, zu bestimmen, wie weit es gehen würde, und jede Anweisung wird protokolliert, einschließlich einer in der letzten Zeile der Funktion. Interessant ist, dass, wenn die Nullreferenceexception auftritt, wird die Anweisung nach dem Funktionsaufruf nicht protokolliert werden:NullReferenceException, wenn Funktion zurückgibt

// ... 
    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 
} 

Die Ausnahme nicht jedes Mal tritt die Funktion aufgerufen wird. Ist es möglich, dass der Stapel entweder vor oder während der Ausführung der Funktion beschädigt wird, sodass die Rücksendeadresse verloren geht, was zur Nullreferenz führt? Ich hätte nicht gedacht, dass sowas unter .NET möglich ist, aber ich denke, es sind merkwürdigere Dinge passiert.

Ich habe versucht, den Aufruf der Funktion mit dem Inhalt der Funktion zu ersetzen, so Inline alles geschieht, und die Ausnahme auftritt dann auf einer Linie, die wie folgt aussieht:

foreach (ClassItem item in classItemCollection) 

ich durch Protokollierung vergewissert haben, dass Die "classItemCollection" ist nicht null, und ich habe auch versucht, die foreach zu einem for zu ändern, falls der IEnumerator etwas Lustiges macht, aber die Ausnahme tritt in der gleichen Zeile auf.

Irgendwelche Ideen zur weiteren Untersuchung?

Aktualisierung: Mehrere Antwortende haben mögliche Lösungen vorgeschlagen, die damit zu tun haben, sicherzustellen, dass der Logger nicht null ist. Um dies klarzustellen, wurden die Protokollierungsanweisungen zu Debugzwecken hinzugefügt, nachdem die Ausnahme ausgelöst wurde.

+0

Kann der Code der Funktion hier enthalten sein? Könnte es leichter machen zu erkennen, was passiert. –

+0

> "... ist es möglich, dass der Stapel beschädigt wird ...?"

+1

re "eleven" wird nicht protokolliert - vielleicht wird es gerade nicht geleert? –

Antwort

2

Ich fand meine Nullreferenz. Wie Fredrik und Micahtan vorgeschlagen haben, habe ich nicht genug Informationen für die Community bereitgestellt, um eine Lösung zu finden, also dachte ich mir, ich sollte posten, was ich gefunden habe, nur um das zu beruhigen.

Dies ist eine Darstellung dessen, was geschah:

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; 

Das Problem stellte sich heraus, meine Verwendung der „etwas“ Variable als eine vorübergehende starke Referenz zu sein, bis die Update-Methode eine permanente erhalten. Der Compiler im Release-Modus optimiert das "something = m_object.Add();" Zuweisung, da "etwas" nicht verwendet wird, bis es erneut zugewiesen wird. Dadurch konnte das ISomething als Garbage Collected erfasst werden, sodass es in m_collection nicht mehr existierte, als ich versuchte, darauf zuzugreifen.

Alles, was ich tun musste, war sicherzustellen, dass ich bis nach dem Aufruf von Update eine starke Referenz hielt.

Ich bezweifle, dass dies für irgendjemanden von Nutzen sein wird, aber falls jemand neugierig war, wollte ich diese Frage nicht unbeantwortet lassen.

2

Die Tatsache, dass es anmeldet „zehn“ würde mich zuerst bei aussehen:

  • ist logger jemals vergeben ... ist dies vielleicht null immer irgendwie
  • ist der Fehler innerhalb Log selbst

Schwer zu sagen, ohne genug Kontext für beide - aber so würde ich es untersuchen. Sie könnten auch einen einfachen null Test irgendwo hinzufügen; als frecher Ansatz, könnten Sie die Log Methode, um etwas anderes, umbenennen und eine Erweiterungsmethode hinzufügen:

[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); 
     } 
    } 
} 

Ihr vorhandener Code die neue Log Erweiterungsmethode nennen soll, und die Ausnahme wird es genau machen klar, wo es barfed . Vielleicht änderst du es mal wieder zurück ... oder lass es vielleicht.

+0

Ich habe die Protokollierungsaufrufe hinzugefügt, nachdem die Ausnahme angezeigt wurde, um herauszufinden, was passiert ist. Bestimmte Platzierungen von Protokollierungsanweisungen scheinen das Problem verschwinden zu lassen, was darauf hindeutet, dass dies mit dem Timing zusammenhängt. –

0

Ändern Sie classItemCollection von mehreren Threads? Wenn Sie die Auflistung in einem anderen Thread ändern, können Sie den Iterator ungültig machen, was zu Ihrer Ausnahme führen könnte. Möglicherweise müssen Sie den Zugriff mit einer Sperre schützen.

bearbeiten: Können Sie weitere Informationen über die Typen von ClassItem und classItemCollection posten?

Eine andere Möglichkeit ist, dass ClassItem ein Werttyp ist und classItemCollection eine generische Auflistung ist und irgendwie eine Null zur Sammlung hinzugefügt wird.Das Folgende wirft eine 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); 
     } 

Dieses besondere Problem kann durch Int behoben werden? i oder Object i in der foreach oder mit einem generischen Container.

+0

classItemCollection ist lokal für die Funktion. –

0

Zustimmen mit Fredrik - weitere Details sind notwendig. Ein Ort, an den man vielleicht denken könnte: Sie erwähnen Multi-Thread-Anwendungen und den Fehler, der bei der Veröffentlichung auftritt, aber nicht beim Debuggen. Möglicherweise tritt ein Timing-Problem auf, bei dem mehrere Threads auf dieselben Objektreferenzen zugreifen.

Egal, würde ich wahrscheinlich auch ein gesagt:

Debug.Assert(classItemCollection != null); 

direkt vor der Schleifeniterationslatenzzeit. Es wird Ihnen im Freigabemodus nicht helfen, aber es kann Ihnen helfen, das Problem zu finden, wenn (wenn?) Es in Debug passiert.

+0

Ich lege ein "if (classItemCollection! = Null)" um die foreach, und die Ausnahme tritt immer noch auf. Es ist wirklich nicht möglich, dass diese Sammlung null ist. Es kann leer sein, aber nicht null. –

+0

Wenn die Auflistung nicht null ist, bin ich mir nicht sicher, warum Sie eine NRE für diese Codezeile erhalten. Ich bin neugierig, was das Problem ist ... (ist es schlimm, wenn du solche Dinge debuggen willst?) – micahtan

0

Ich würde nach Code suchen, der den Logger oder einen seiner Abhängigen auf null setzt. Gibt es Eigenschaften von Logger, die, wenn sie auf null gesetzt sind, dies auslösen könnten? Der Freigabemodus beschleunigt manchmal die Anwendungsausführung, was Synchronisationsprobleme aufdecken kann, die durch die Leistungseinbußen des Debug-Modus und/oder des Debuggers maskiert werden.

0

Die Tatsache, dass "eleven" nicht eingeloggt wird, lässt mich glauben, dass der Logger gerade vor dem Aufruf auf Null gesetzt wird. Kannst du es in einen try/catch einpacken und sehen, ob es den Fangteil des Blocks trifft? Vielleicht können Sie eine MessageBox.Show einfügen oder etwas in eine bekannte Datei schreiben, wenn das passiert.