2017-04-24 4 views
6

Ich möchte große XML-Dokumente in XDocument-Objekte laden. Der einfache synchrone Ansatz mit XDocument.Load(path, loadOptions) funktioniert super, blockt aber beim Laden großer Dateien (besonders aus dem Netzwerkspeicher) für eine ungemütlich lange Zeit im GUI-Kontext.XDocument asynchron laden

Ich schrieb diese Async-Version mit der Absicht, die Reaktionszeit beim Laden von Dokumenten zu verbessern, insbesondere beim Laden von Dateien über das Netzwerk.

public static async Task<XDocument> LoadAsync(String path, LoadOptions loadOptions = LoadOptions.PreserveWhitespace) 
    { 
     String xml; 

     using (var stream = File.OpenText(path)) 
     { 
      xml = await stream.ReadToEndAsync(); 
     } 

     return XDocument.Parse(xml, loadOptions); 
    } 

jedoch auf einem 200 MB XML RAW-Datei von der lokalen Festplatte geladen wird, vervollständigt die synchrone Version in wenigen Sekunden. Die asynchrone Version (läuft in einem 32-Bit-Kontext) statt wirft ein OutOfMemoryException:

at System.Text.StringBuilder.ToString() 
    at System.IO.StreamReader.<ReadToEndAsyncInternal>d__62.MoveNext() 

Ich stelle mir dies wegen der temporären String-Variable verwendet, um die XML-Rohdaten im Speicher zu halten durch die XDocument zum Parsen. Vermutlich ist es im synchronen Szenario möglich, XDocument.Load() durch die Quelldatei zu streamen, und es muss nie ein einziger großer String erstellt werden, der die gesamte Datei enthält.

Gibt es eine Möglichkeit, das Beste aus beiden Welten zu bekommen? Laden Sie die XDocument mit vollständig asynchroner E/A, und ohne eine große temporäre Zeichenfolge erstellen zu müssen?

+0

Vielleicht sollten Sie 'XDocument.Load (Strom)'? – DavidG

+0

Wie würde das die Ladeoperation asynchron machen? – Hydrargyrum

+0

Nun, das wäre nicht selbst, aber es würde die String-Variable, die Sie hier haben, und hoffentlich die OOM-Ausnahme beseitigen. – DavidG

Antwort

2

Zuerst wird die Aufgabe nicht asynchron ausgeführt. Sie müssten entweder einen integrierten asynchronen IO-Befehl verwenden oder eine Aufgabe im Thread-Pool selbst ausführen. Zum Beispiel

public static async Task<XDocument> LoadAsync 
(String path 
, LoadOptions loadOptions = LoadOptions.PreserveWhitespace 
) 
{ 
    return Task.Run(()=>{ 
    using (var stream = File.OpenText(path)) 
     { 
      return XDocument.Load(stream, loadOptions); 
     } 
    }); 
} 

und wenn Sie die stream version von Parse verwenden dann müssen Sie nicht eine temporäre Zeichenfolge bekommen.

+3

Ok. Das habe ich in meinem letzten Kommentar zu der Frage dargelegt. Dies wird also einen Thread-Pool-Thread verwenden, um den implizit erforderlichen I/O zu steuern, da das XDocument seinen Weg durch den Stream kaut. Und diese E/A wird den Worker-Thread der Task sporadisch blockieren. Sieht so aus, als wäre dies das Beste, was man tun kann, wenn keine echte XDocument.LoadAsync() - Implementierung vorhanden ist, die korrekte Async-I/O-Anweisungen unter der Haube verwendet. Ich sehe keinen Vorteil, um File.OpenText jedoch explizit aufzurufen.Kann auch einfach XDocument.Load aufrufen (Pfad) – Hydrargyrum

+0

Wenn Sie zehntausende von XDocuments auf einem Server parallel gelesen haben, könnten Sie besorgt sein, einen Thread aus dem Thread-Pool zu stehlen, anstatt echte async IO zu verwenden, aber das ist wirklich ein Problem ? – bradgonesurfing

+1

Wahrscheinlich nicht. Daher mein Kommentar, dass es wahrscheinlich gut genug ist. Ich habe aufgestanden und trotzdem akzeptiert – Hydrargyrum