1

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

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

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

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

这段代码具有以下形式(为清楚起见进行了简化):

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是分开的。

最多可以有 1000 个项目,每个项目都有处理的ICollection<TheClass>属性和子集合,生成许多SELECTUPDATE语句(使用“NHibernate Profiler”确认)

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

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

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

或者

  1. 使用 SQL 批量复制

Parallel.ForEach我还阅读了很多关于 SQL Server 死锁和容易陷入陷阱的好信息 :

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

1 回答 1

1

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

在死锁的情况下重试。

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

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

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

于 2016-03-01T21:03:10.527 回答