我使用大 XML文件(~2Go),到目前为止,阅读方式是这样做的:
private void readParameters(XmlReader m, Measurement meas)
{
while (m.ReadToFollowing("PAR"))
{
XmlReader par = m.ReadSubtree();
readParameter(par, meas);
par.Close();
((IDisposable)par).Dispose();
}
}
哪个进展顺利,但是懒散了。所以我把我的科学带进来,试图将阅读并行化:
private void readParameters(XmlReader m, Measurement meas)
{
List<XmlReader> readers = new List<XmlReader>();
while (m.ReadToFollowing("PAR"))
{
readers.Add(m.ReadSubtree());
}
Parallel.ForEach(readers, reader =>
{
readParameter(reader, meas);
reader.Close();
((IDisposable)reader).Dispose();
}
);
}
但它在foreach
的每次迭代中读取相同的节点。我怎样才能解决这个问题?这甚至是阅读并行化的好方法吗?
答案 0 :(得分:1)
因为,正如ReadSubtree的评论所写:
只能在元素节点上调用ReadSubtree。读取整个子树后,对Read方法的调用返回false。 当新的XmlReader关闭时,原始的XmlReader将位于子树的EndElement节点上。因此,如果您在book元素的start标记上调用了ReadSubtree方法,那么已读取子树并关闭了新的XmlReader,原始XmlReader位于book元素的结束标记上。 在新的XmlReader关闭之前,您不应对原始XmlReader执行任何操作。此操作不受支持,可能导致不可预测的行为。
显然,这种方法不是线程安全的。你不能放弃&#34;一些ReadSubtree()
然后在你试图做的时候再使用它们。
一般来说,考虑到XmlReader
显然,你无法做你想做的事。一般情况下,表示一个读者,它提供对XML数据的快速,非缓存,仅向前访问。
Stream
XmlReader
正在使用的Stream
可能仅限前向,因此克隆它需要XmlReader
为&#34;分叉&#34; (一个&#34;复制&#34;对于Stream
的每个克隆)(XmlReader
无法保证可能的事情)或XmlReader
缓存节点(某些东西保证不由List<XElement> elements = new List<XElement>();
while (m.ReadToFollowing("PAR"))
{
elements.Add(XElement.Load(m.ReadSubtree()));
}
Parallel.ForEach(elements, el =>
{
});
)完成
根据@mike z的建议,你可以
XDocument
但是我不确定这会改变什么,除了你的内存使用(观看超过2GB的内存消失:-)),因为现在整个Xml解析都是在&#34; main& #34;线程,所有PAR元素都在public sealed class MyClass : IEnumerable<XElement>, IDisposable
{
public readonly XmlReader Reader;
public MyClass(XmlReader reader)
{
Reader = reader;
}
// Sealed class
public void Dispose()
{
Reader.Dispose();
}
public IEnumerator<XElement> GetEnumerator()
{
while (Reader.ReadToFollowing("PAR"))
{
yield return XElement.Load(Reader.ReadSubtree());
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
private static void readParameters(XmlReader m, Measurement meas)
{
var enu = new MyClass(m);
Parallel.ForEach(enu, reader =>
{
// You do the work here
});
}
个对象中读取。
或者你可能会尝试:
Parallel.ForEach
现在MyClass
懒散地被一个普通人{{1}}(请原谅我名字:-))懒散地加载了子树。