[英]Load XDocument asynchronously
我想將大型 XML 文檔加載到 XDocument 對象中。 使用XDocument.Load(path, loadOptions)
的簡單同步方法效果很好,但在加載大文件(尤其是來自網絡存儲)時,在 GUI 上下文中會阻塞很長時間。
我編寫這個異步版本的目的是提高文檔加載的響應能力,特別是在通過網絡加載文件時。
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);
}
但是,在從本地磁盤加載的 200 MB XML 原始文件上,同步版本會在幾秒鍾內完成。 異步版本(在 32 位上下文中運行)反而拋出OutOfMemoryException
:
at System.Text.StringBuilder.ToString()
at System.IO.StreamReader.<ReadToEndAsyncInternal>d__62.MoveNext()
我想這是因為臨時字符串變量用於將原始 XML 保存在內存中以供XDocument
解析。 大概在同步場景中, XDocument.Load()
能夠通過源文件流式傳輸,並且永遠不需要創建單個巨大的 String 來保存整個文件。
有什么辦法可以兩全其美嗎? 使用完全異步 I/O 加載XDocument
,而無需創建大型臨時字符串?
XDocument.LoadAsync()
在 .NET Core 2.0 中可用: https : XDocument.LoadAsync()
2.0
遲到的答案,但我也需要在“舊版”.NET Framework 版本上進行異步讀取,所以我想出了一種方法來真正以異步方式讀取內容,而無需恢復到在內存中緩沖 XML 數據。
由於XDocument.CreateWriter()
提供的 writer 不支持異步寫入,因此XmlWriter.WriteNodeAsync()
失敗,代碼執行異步讀取並將其轉換為 XDocument-writer 上的同步寫入。 然而,代碼的靈感來自XmlWriter.WriteNodeAsync()
工作方式。 由於作者構建了一個內存中的 DOM,這實際上比實際進行異步寫入更好。
public static async Task<XDocument> LoadAsync(Stream stream, LoadOptions loadOptions) {
using (var reader = XmlReader.Create(stream, new XmlReaderSettings() {
DtdProcessing = DtdProcessing.Ignore,
IgnoreWhitespace = (loadOptions&LoadOptions.PreserveWhitespace) == LoadOptions.None,
XmlResolver = null,
CloseInput = false,
Async = true
})) {
var result = new XDocument();
using (var writer = result.CreateWriter()) {
do {
switch (reader.NodeType) {
case XmlNodeType.Element:
writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI);
writer.WriteAttributes(reader, true);
if (reader.IsEmptyElement) {
writer.WriteEndElement();
}
break;
case XmlNodeType.Text:
writer.WriteString(await reader.GetValueAsync().ConfigureAwait(false));
break;
case XmlNodeType.CDATA:
writer.WriteCData(reader.Value);
break;
case XmlNodeType.EntityReference:
writer.WriteEntityRef(reader.Name);
break;
case XmlNodeType.ProcessingInstruction:
case XmlNodeType.XmlDeclaration:
writer.WriteProcessingInstruction(reader.Name, reader.Value);
break;
case XmlNodeType.Comment:
writer.WriteComment(reader.Value);
break;
case XmlNodeType.DocumentType:
writer.WriteDocType(reader.Name, reader.GetAttribute("PUBLIC"), reader.GetAttribute("SYSTEM"), reader.Value);
break;
case XmlNodeType.Whitespace:
case XmlNodeType.SignificantWhitespace:
writer.WriteWhitespace(await reader.GetValueAsync().ConfigureAwait(false));
break;
case XmlNodeType.EndElement:
writer.WriteFullEndElement();
break;
}
} while (await reader.ReadAsync().ConfigureAwait(false));
}
return result;
}
}
首先,任務不是異步運行的。 您需要使用內置的異步 IO 命令或自己在線程池上啟動任務。 例如
public static Task<XDocument> LoadAsync
( String path
, LoadOptions loadOptions = LoadOptions.PreserveWhitespace
)
{
return Task.Run(()=>{
using (var stream = File.OpenText(path))
{
return XDocument.Load(stream, loadOptions);
}
});
}
如果您使用 Parse 的流版本,則不會獲得臨時字符串。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.