Parallel.ForEach 与 NHibernate 一起使用,导致 SQL Server 锁定
本文关键字:导致 SQL Server 锁定 ForEach NHibernate 一起 Parallel | 更新日期: 2023-09-27 18:37:04
首先,我不是多线程和并行编程方面的专家。
我正在尝试优化遗留应用程序的性能(.Net 4
,NHibernate 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 个项目,每个项目都有处理的属性和子集合,生成许多SELECT
和UPDATE
语句(使用"NHibernate 探查器"确认)
有没有更好的方法来并行处理这个问题,或者我应该将代码重构为传统循环?
我知道我也可以使用以下方法实现我的代码:
- 同一
ISession
上下文中的foreach
循环 - 使用无状态会话
- 将
Environment.BatchSize
设置为合理的值
或
- 使用 SQL 大容量复制
我还阅读了很多关于SQL Server死锁的好信息,Parallel.ForEach
这是一个容易的陷阱:
- SQL 事务已死锁
- 使用 SQL 大容量复制作为替代方法
- 数据和任务并行性中的潜在陷阱
- 具有 SQL Server 数据库调用的多线程 C# 应用程序
这是一个非常复杂的话题。有一种策略可以保证是安全的,并且可能会导致加速:
如果出现死锁,请重试。
由于死锁会回滚事务,因此您可以安全地重试整个事务。如果死锁率较低,则并行加速将很高。
重试的好处是,您可以在一个中心位置进行简单的代码更改。
由于从发布的代码中看不出来:确保线程不共享会话或实体。它们都不是线程安全的。