Parallel.ForEach与NHibernate一起使用导致SQL Server锁定

时间:2016-03-01 17:06:34

标签: c# sql-server multithreading .net-4.0 nhibernate-4

首先,我不是多线程和并行编程方面的专家。

我正在尝试优化遗留应用程序(.Net 4NHibernate 2.1)的性能。

**到目前为止,升级NHibernate不是优先事项,而是正在筹备中。

随着时间的推移,随着数据的增长,性能已成为一场噩梦。我看到的一个项目是一个Parallel.ForEach语句,它调用一个方法来获取和更新一个复杂的实体(具有多个关系 - propeties& collections)。

这段代码具有以下形式(为简洁起见而简化):

void SomeMethod(ICollection<TheClass> itemsToProcess)
{
   Parallel.ForEach(itemsToProcess, item => ProcessItem(item);
}

TheClass ProcessItem(TheClass i)
{
   var temp = NHibernateRepository.SomeFetchMethod(i);
   var result = NHibernateRepository.Update(temp);
   return result;
}

SQL Server间歇性地报告数据库锁定错误,并显示以下错误:

Transaction (Process ID 20) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction

我怀疑这是由于某些竞争条件发生导致死锁,即使ISessions是分开的。

ICollection<TheClass>最多可包含1000个项目,每个项目都包含已处理的属性和子集合,生成许多SELECTUPDATE语句(使用'NHibernate Profiler'确认)< / p>

是否有更好的方法以并行方式处理此问题,还是应该将代码重构为传统循环?

我知道我可以使用以下方式实现我的代码:

  1. 同一foreach上下文
  2. 中的ISession循环
  3. 使用无状态会话
  4. Environment.BatchSize设置为合理的值
  5. OR

    1. 使用SQL BulkCopy
    2. 我还阅读了很多关于SQL Server死锁的信息,Parallel.ForEach很容易陷入困境:

      1. SQL Transaction was deadlocked
      2. Using SQL Bulk Copy as an alternative
      3. Potential Pitfalls in Data and Task Parallelism
      4. Multi threading C# application with SQL Server database calls

1 个答案:

答案 0 :(得分:1)

这是一个非常复杂的话题。有一种策略可以保证安全,可能会导致加速:

在发生死锁时重试。

由于死锁回滚事务,您可以安全地重试整个事务。如果死锁率很低,并行性加速会很高。

重试的好处是你可以在一个中心位置进行简单的代码更改。

由于发布的代码不明显:确保线程不共享会话或实体。它们都不是线程安全的。