【问题标题】:Parallel For Each get in a DeadLockParallel For Each 陷入死锁
【发布时间】:2016-07-18 19:38:28
【问题描述】:

我正在尝试使用 Parrallel For each 循环超过 100 000 行的数据表。在大约 25 000 次迭代中一切正常。我没有收到任何错误,并且我看到应用程序仍在运行,但它有点阻塞并且没有任何反应。我试图将循环封装在 factory.startnew 中,但在大约 5000 次迭代时我会无缘无故地获得随机中止预期。

Dim lstExceptions As New ConcurrentQueue(Of Exception)
Dim options As New ParallelOptions
options.MaxDegreeOfParallelism = 3

Parallel.ForEach(ReservationReportDS.Tables(0).AsEnumerable(), options,
    Sub(row)
        Try
            Dim tmpRow As DataRow = CType(row, DataRow)
            Dim ReservationID As Integer = tmpRow.Field(Of Integer?)("autNoReservation")
            Dim customerID As Integer = tmpRow.Field(Of Integer?)("CustomerID")
            Dim VehiculeID As Integer = tmpRow.Field(Of Integer?)("autNoVehicule")

            Dim bill As New BillingPath()
            bill.Calculate_Billing(ReservationID, customerID, VehiculeID)

        Catch err As Exception
            lstExceptions.Enqueue(err)
        End Try
    End Sub
)

If (lstExceptions.Count > 0) Then
    Throw New AggregateException(lstExceptions)
End If

Catch errAgg As AggregateException
    For Each ex As Exception In errAgg.InnerExceptions
        Log(Log_Billing_UI, "", System.Reflection.MethodBase.GetCurrentMethod().Name & GetExceptionInfo(ex))
    Next
Catch ex As Exception
    Log(Log_Billing_UI, "", System.Reflection.MethodBase.GetCurrentMethod().Name & GetExceptionInfo(ex))
End Try

【问题讨论】:

  • 能否添加错误堆栈跟踪
  • 1) bill.Calculate_Billing 是否写入数据集? 2)经过精心设计和研究,设计了一个数据库来为这类事情工作,因此它可能是一个不错的选择。此外,数据库中的数据是持久的。
  • 没有错误,只是阻塞。在没有线程的情况下使用相同的数据可以正常工作。 1) 是的 Calculate_Billing 将在 95% 的时间内执行 1 次插入,5% 为 2 次或 3 次。但调用会进行多次选择(至少 10 次)

标签: asp.net vb.net parallel-processing task-parallel-library


【解决方案1】:

既然你有这么多的记录,我建议你考虑以下概念:

  1. 首先将所有记录读入 ConcurrentQueue(Of SomeBillingInfoClass) 集合 - 这将允许您不保持与 DB 的连接打开,使用从 DB 读取的数据进行线程安全的休息操作。
  2. 创建包含计费计算代码的任务列表。这将允许您并行运行任务并轻松从 #1 传递 ConcurrentQueue 变量。
  3. 在 ConcurrentQueue 中至少保留一个元素时保持任务循环运行。
  4. 如果您可以将计费计算结果汇总到其他类 - 您可以使用额外的线程安全 ConcurrentQueue(Of BillingCalcResultInfoClass) 集合来实现。
  5. 计算完所有帐单后 - 在单线程和单个长事务中写入数据库 - 这可能比细粒度写入数据库更快。

关于您的代码的一些注释 - 我认为您可能不需要手动抛出 AggregateException - .Net 环境会自动为您完成。您只需要在任务的 .ContinueWith() 方法中捕获它(抱歉,我主要是 c# 开发人员并使用 c# 表示法)。

我使用类似的方法来处理数百万条记录,并且效果很好。通常我使用 3-5 个任务。但是您可以随时了解您可能有多少任务。

使用 ConcurrentQueue 或类似的线程安全集合可以让您更轻松地保持代码线程安全。

如果您有任何问题,请告诉我。

【讨论】:

  • 我尝试使用任务列表,它现在看起来可以工作。对于每个条目,我现在正在创建一个任务,它将 cpu 使用率提高到 100%。我尝试创建一个只包含几个任务的算法,看看它是否有效
  • @Max_Thom,是的。您可以加载某个线程安全集合中的所有记录。然后运行多个任务,并在每个任务内部调用 collection.TryDequeue 方法以弹出下一条记录。这些任务应该在无限循环中运行,只有在集合中没有记录时才会中断。这样您就可以同时处理多个任务,而不会占用 100% 的 CPU。
【解决方案2】:

感谢大家的回答,尤其是 Anton Norko。我终于找到了问题所在,它就在我身边。在某些情况下,Calculate_Billing 会陷入无限循环。由于我同时使用了3个线程,所以它们都被一个一个卡住了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-03
    • 2012-07-27
    • 2020-06-26
    • 2015-08-31
    • 1970-01-01
    相关资源
    最近更新 更多