异步加载XDocument
我想将大型XML文档加载到XDocument对象中。 使用XDocument.Load(path, loadOptions)
的简单同步方法效果很好,但是在加载大文件(特别是从网络存储)时在GUI上下文中阻塞了很长时间。异步加载XDocument
我写了这个异步版本,旨在提高文档加载的响应速度,特别是通过网络加载文件时。
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()
能够通过源文件进行流式传输,并且从不需要创建一个巨大的字符串来保存整个文件。
有没有什么办法让两全其美?加载XDocument
完全异步I/O,而不需要创建一个大的临时字符串?
首先,任务不是异步运行的。您需要使用内置的异步IO命令或自己启动线程池上的任务。例如
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);
}
});
}
如果您使用Parse的stream version,那么您不会得到一个临时字符串。
好的。这就是我在对问题的最终评论中所概述的内容。因此,这将使用线程池线程来驱动隐式所需的I/O,因为XDocument在流中咀嚼。而且I/O本身会零星地阻塞Task的工作线程。 看起来这是最好的,可以做到的,因为没有一个真正的XDocument.LoadAsync()实现,它使用适当的异步I/O指令。 虽然我没有看到明确调用File.OpenText的好处。也可以直接调用XDocument.Load(path) – Hydrargyrum
如果你正在并行读取服务器上数以千计的XDocuments 10s,你可能会担心从线程池中窃取线程而不是使用真正的异步IO,但这真的是一个问题? – bradgonesurfing
可能不是。因此,我的评论是这可能够好。无论如何我都赞成并接受 – Hydrargyrum
XDocument.LoadAsync()
可在.NET核2.0:https://docs.microsoft.com/en-us/dotnet/api/system.xml.linq.xdocument.loadasync?view=netcore-2.0
也许你应该使用'XDocument.Load(流)'? – DavidG
这会如何使加载操作异步? – Hydrargyrum
那么它本身不会,但它会消除你在这里的字符串变量,并希望OOM异常。 – DavidG