【问题标题】:VB.Net Use XmlNodeList in a parallel ForEachVB.Net 在并行 ForEach 中使用 XmlNodeList
【发布时间】:2016-12-21 14:23:35
【问题描述】:

我有一段代码遍历 XmlNodeList 中的节点,并根据节点名称为每个节点创建不同的对象,并将其添加到列表中以供打印。

For Each node as XmlNode In nodeList
    Select Case node.Name.ToUpper()
        Case "SHAPE"
            _items.Add(New ShapeTemplate(node, Me))
        Case "TEXTBLOCK"
            _items.Add(New TextblockTemplate(node, Me))
    End Select
Next

此代码运行良好,但由于 ShapeTemplate 和 TextblockTemplate 构造函数必须完成所有工作,因此速度非常慢。由于订单对象添加到 _items 并不重要,我认为加快它的好方法是使用 parallel.ForEach 循环。问题是 XmlNodeList 不能与 parallel.ForEach 一起使用,因为它是一个非泛型集合。我一直在寻找将 XmlNodeList 转换为 List(Of XmlNode) 的方法,但没有成功。我不断看到的答案是

Dim nodes as New List(Of xmlNode)(nodeList.Cast(Of xmlNode)())

但是当我尝试它时,我收到一条错误消息,告诉我“Cast”不是 XmlNodeList 的成员。

我也尝试过像这样使用 TryCast

Dim nodes as List(Of XmlNode) = TryCast(CObj(nodeList), List(Of XmlNode))

但它会导致节点为 Nothing,因为无法转换对象。

有人知道我如何在 parallel.ForEach 循环中使用 XmlNodeList 吗?

编辑:如果可以的话,我会尽量避免使用循环进行转换

【问题讨论】:

  • 你问如何在循环中做,然后说你想避免使用循环?为什么?
  • 在创建 ShapeTemplate 和 TextblockTemplate 时尝试使用线程。这不应该阻止循环。
  • 您是否有对 System.Core.dll 的引用和对 System.Linq 的导入,以便您选择 Enumerable.Cast?此外,[PLINQ] 可能比 Parallel.ForEach 更适合。
  • Daniel,我想使用 parallel.ForEach 循环代替问题中包含的循环,但我不想循环 XmlNodeList 以将其转换为可用格式在进入parallel.ForEach循环之前
  • 马克,你能指导我去哪里看看如何使用 PLINQ 而不是 Parallel.ForEach 吗?我确实有你提到的要求

标签: vb.net foreach casting parallel-processing xmlnodelist


【解决方案1】:

您可以使用Parallel LINQ 而不是Parallel.ForEach,这似乎更适合这种转换。这看起来就像一个普通的 LINQ 查询,但添加了 .AsParallel()

Imports System.Linq

' _items will not be in same order as nodes in nodeList.
' Add .AsOrdered() after .AsParallel() to maintain order, if needed.
_items = (
    From node In nodeList.Cast(Of XmlNode)().AsParallel()
    Select item = CreateTemplate(node)
    Where item IsNot Nothing
).ToList()

Function CreateTemplate(node As XmlNode) As ITemplate ' interface or base class for your types
    Dim item As ITemplate
    Select Case node.Name.ToUpper()
        Case "SHAPE"
            item = New ShapeTemplate(node, Me)
        Case "TEXTBLOCK"
            item = New TextblockTemplate(node, Me)
        Case Else
            item = Nothing
    End Select
    Return item
End Function

【讨论】:

    【解决方案2】:

    正如here 所见,XmlNodeList 可以通过将OfType 传递给构造函数来转换为通用List(Of XmlNode)

    ' I added so your code could compile.
    ' I assumed an interface shared by ShapeTemplate and TextblockTemplate
    Dim nodeList As XmlNodeList
    Dim _items As New List(Of ITemplate)
    
    Dim _nodes As New List(Of XmlNode)(nodeList.OfType(Of XmlNode))
    

    现在是并行循环。请注意,如果您要添加到非线程安全集合,例如List,您将需要同步将对象添加到列表中。所以我将耗时部分(模板构造函数)与快速操作(添加到列表)分开。这应该会提高您的表现。

    Dim oLock As New Object
    Parallel.ForEach(
        _nodes,
        Sub(node)
            Dim item As ITemplate
            Select Case node.Name.ToUpper()
                Case "SHAPE"
                    item = New ShapeTemplate(node, Me)
                Case "TEXTBLOCK"
                    item = New TextblockTemplate(node, Me)
                Case Else
                    item = Nothing ' or, do something else?
            End Select
            SyncLock oLock
                _items.Add(item)
            End SyncLock
        End Sub)
    

    【讨论】:

    • 谢谢+1。这对我有用。我不知道我需要导入 System.Linq,但是一旦我这样做了,它就可以正常工作了。我确实将 _items 从 List(Of T) 更改为 ConcurrentStack(Of T)。它为我提供了所需的功能,而无需将并行执行与 SyncLock 捆绑在一起。
    • 太棒了。请注意:锁定并添加到列表可能不是瓶颈,因此如果这是您原始设计的一部分,您应该能够保留List。我编写它的方式允许感知的长时间运行的操作并行运行。此外,ConcurrentStack 是一个 LIFO(后进先出)集合,因此它不能完全替代 List。不过,这不是重点,很高兴答案有所帮助。
    猜你喜欢
    • 1970-01-01
    • 2013-07-17
    • 1970-01-01
    • 1970-01-01
    • 2015-01-10
    • 2016-03-05
    • 2015-04-16
    • 2014-08-22
    相关资源
    最近更新 更多