【问题标题】:Deadlock in Parallel.Foreach while using ExecuteNonQuery?使用 ExecuteNonQuery 时 Parallel.Foreach 死锁?
【发布时间】:2020-05-08 06:28:07
【问题描述】:

我在使用 Parallel.Foreach 时遇到死锁错误。 我在数据表中有 1000 条记录,我创建了 5 个线程来处理它。 但是当我运行这个控制台应用程序时,在处理了一些记录之后,它会创建一个死锁,并且不会处理其他记录。 这是我的代码:

Parallel.ForEach(dt1.AsEnumerable(), new ParallelOptions { MaxDegreeOfParallelism = 5 }, dr =>
{
    cmd1.CommandText = $"Update AuditMessage set Status=1" +
        $" where SXAEASCoreAuditMessageID ='{Convert.ToString(dr["AuditMessageID"])}' ";
    cmd1.CommandType = CommandType.Text;
    cmd1.Transaction = trans;
    cmd1.ExecuteNonQuery();                          
});

【问题讨论】:

  • 因此,在多个线程中,您正在尝试修改相同的 SqlCommand 对象。这不就像 5 个人试图同时驾驶同一辆车,每个人都有不同的目的地吗?
  • 你有什么 DBMS ?
  • 你需要重新考虑这一点,对于每个并行不适合 IO 绑定的工作负载开始,另外在数据库中抛出更多线程很少能解决任何问题。数据库在内部是线程化的,并且在分配资源方面比您(客户端)更有效。因此,这变成了 X/Y 问题……您有一个要解决的问题,而实际上您的问题实际上是其他问题。沿着这条道路前进将是一个等待发生的重构。我建议为数据库制作一个更智能的查询,然后将线程全部丢弃并使用异步

标签: c# sql multithreading parallel-processing


【解决方案1】:

为什么会锁定?

您的问题可能不是数据库并发,而是您用于每个线程的 cmd1 对象上的并发。

您需要为每个线程或在每次迭代中创建一个新的 SqlCommand。

Parallel.ForEach(dt1.AsEnumerable(), new ParallelOptions { MaxDegreeOfParallelism = 5 }, dr =>
    {
                  using(var cmd = new SqlCommand(connection))
                  {
                     cmd.CommandText = $"Update AuditMessage set Status=1 where SXAEASCoreAuditMessageID ='{Convert.ToString(dr["AuditMessageID"])}' ";
                     cmd.CommandType = CommandType.Text;
                     cmd.Transaction = trans;
                     cmd.ExecuteNonQuery();
                  }                          
    });

有关行/表锁定的一些基础知识。

如果您在 SQL Server 上进行操作,那么您所创建的开销将超过此并行处理的速度。修改的每一行都会被 DBMS 锁定,最终,当开销变高时,它会开始锁定表,让您的线程继续按顺序工作。

所以,这里忘了parallel.foreach,让数据库做效率优化

您可以将 AuditMessageIds 选择到列表中。 然后将其切成 100 块左右,然后进行更智能的更新查询,例如批量更新到状态 1 的查询..

where SXAEASCoreAuditMessageID IN ...

【讨论】:

  • 我正在使用 SQL Server 2017,当我尝试了上面给出的解决方案时,它可以工作,但不是我的所有记录都得到更新,在上面 parralel.foreach 我正在使用 sql 事务并从中选择数据数据库,然后我将所选数据传递到 parallel.foreach 列表中,并且这些记录需要使用 status = 1 进行更新,但是在尝试了您的解决方案之后,我的所有记录都没有更新。
  • 请建议,我想并行使用sql事务。foreach,如何使用?
  • 我赞成你的回答,虽然我不确定你所做的是否合法。根据微软的SqlConnectionis not thread safe。因此Parallel.ForEach lambda 中的new SqlCommand(connection) 行可能是非法的。
  • @TheodorZoulias,我认为在这种情况下它是合法的,因为每个线程有一个 SqlCommand,但是,由于后面的代码使用相同的连接,你可能是对的,而且,我怀疑parallel.foreach 在这里加快了速度,因为afaik,无论如何它都被连接顺序化了。我在原始答案中写了这个,但为了让答案更简单而把它删掉了
猜你喜欢
  • 1970-01-01
  • 2023-03-03
  • 2018-09-18
  • 2023-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多