并行化XML读错了

时间:2015-03-17 15:35:41

标签: c# xml parallel-processing

我使用 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的每次迭代中读取相同的节点。我怎样才能解决这个问题?这甚至是阅读并行化的好方法吗?

1 个答案:

答案 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}}(请原谅我名字:-))懒散地加载了子树。