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

本文关键字:导致 SQL Server 锁定 ForEach NHibernate 一起 Parallel | 更新日期: 2023-09-27 18:37:04

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

我正在尝试优化遗留应用程序的性能(.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 探查器"确认)

有没有更好的方法来并行处理这个问题,或者我应该将代码重构为传统循环?

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

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

  1. 使用 SQL 大容量复制

我还阅读了很多关于SQL Server死锁的好信息,Parallel.ForEach这是一个容易的陷阱:

  1. SQL 事务已死锁
  2. 使用 SQL 大容量复制作为替代方法
  3. 数据和任务并行性中的潜在陷阱
  4. 具有 SQL Server 数据库调用的多线程 C# 应用程序

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

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

如果出现死锁,请重试。

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

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

由于从发布的代码中看不出来:确保线程不共享会话或实体。它们都不是线程安全的。