2016-12-14 6 views
3

Ich habe einen Codeblock, der aus einer Warteschlange liest, ein Element verarbeitet (in seinem eigenen Thread) und dann wiederholt, bis die Warteschlange leer ist.Ich kann nicht verstehen, warum ich eine NullReferenceException erhalte

StartSync() entfernt entweder ein Element aus der Warteschlange oder gibt null zurück, wenn die Warteschlange leer ist. GetOrders() verarbeitet das Objekt. Das Problem ist, dass der Code manchmal eine NullReferenceException in dieser Zeile auslöst Task.Run (() => GetOrders (sync.SyncId, customerId))

Im Debugger ist Sync null (Grund für Ausnahme), aber CustomerId hat einen Wert. Das sagt mir Sync hatte einen Wert auf der vorherigen Zeile. Das verwirrt mich, ich denke, es hat etwas mit Task.Run und Threading zu tun, aber ich verstehe nicht, wie eine lokal begrenzte Variable ihren Wert spontan ändert.

+0

Wenn Ihr sync = _common.StartSync() vor Ihrem Sync-Objekt ungültig und der GetOrders() beendet ist, es macht Sinn, warum es das tun würde. Können Sie GetOrders und GetOrdersAsync() als tatsächliche Async-Methoden erstellen und dann auf GetOrders warten? Es wird dann warten, bevor Sie versuchen, die nächste Synchronisierung zu verarbeiten. – Dispersia

Antwort

6

Sie aktualisieren die Referenz von sync, bevor Ihre Aufgabe eine Chance erhält, darauf zu arbeiten. Beachten Sie, dass Aufgaben nicht unbedingt sofort beginnen. In einigen Fällen kann Ihre Aufgabe starten, nachdem die folgenden weiter unten ausgeführt wird:

sync = _common.StartSync(); 

Jetzt Ihr Verweis auf sync potentiell null ist, und wenn Ihre Aufgabe sync.SyncId zuzugreifen geht, können Sie eine NULL-Verweis Ausnahme erhalten.

Ihr Code wie folgt ändern:

if (result) { 
    var syncId = sync.SyncId; 
    Task.Run(() => GetOrders(syncId, customerId)); 
} 

Dies funktioniert, weil wir in der Id passieren wollen einfach nur. Was, wenn Sie das Objekt selbst einreichen wollten? Sie müssen eine neue Variable erstellen, das wird nicht außerhalb des Verschlusses geändert werden:

if (result) { 
    var capturedSync = sync; 
    //Assuming GetOrders now takes a `Sync` 
    Task.Run(() => GetOrders(capturedSync, customerId)); 
} 
+1

Für Ihr letztes Beispiel, das ist eine flache Kopie. Durch die Änderung der Synchronisierung wird auch capturedSync geändert. Er würde eine tiefe Kopie machen müssen, in der ich einfach zu Ihrem ersten Beispiel bleiben würde. – Dispersia

+2

@Dispersia "Durch die Änderung der Synchronisierung wird auch capturedSync geändert" ... und Sie sind sich absolut sicher? – spender

+0

@spender ja? das ist was eine flache Kopie tut. Probieren Sie es selbst aus, erstellen Sie ein Objekt, weisen Sie es zu (dies wird durch Bezugnahme geschehen), ändern Sie dann das neue Objekt und sehen Sie auch die ursprüngliche Änderung. – Dispersia

Verwandte Themen