【问题标题】:NHibernate: Cross thread lazy loadingNHibernate:跨线程延迟加载
【发布时间】:2011-03-10 08:24:58
【问题描述】:

我编写了一个应用程序,它使用 NHibernate 来提供底层对象持久性模型。

数据模型由一个对象组成,该对象具有多个字符串属性和一个用于存储任意数据的 HashSet 属性。这在 NHibernate 中表示为一个附加了一个集合的类。

应用程序的核心是在线程池中运行的业务规则引擎。成功执行规则集后,我请求 NHibernate 更新数据库中的对象。

因为它是多线程的,所以我每次进行数据库访问时都会调用 .OpenSession() 以返回一个新的 ISession 对象来完成我的工作,并在使用后将其处理掉。

不幸的是,这意味着我无法利用延迟加载 - 就像我启用它一样,我到处都会收到延迟加载异常错误。

我已经尝试过使用互斥锁来锁定线程之间的单个 NHibernate 会话并打开延迟加载。这有效,但速度非常慢。分析表明,等待互斥体花费了大量时间 - 这会破坏应用程序的并行性。

有没有办法在我的情况下启用延迟加载? IE。跨线程数据库访问。

或者还有其他方法可以解决这个问题吗?每次规则执行完成时可能不会显式触发更新?虽然我这样做的主要原因之一是如果应用程序失败,DB 代表应用程序中数据的最后状态。

您可以提供的任何帮助/指示将不胜感激。

【问题讨论】:

  • 我在答案中添加了一个部分。

标签: .net nhibernate


【解决方案1】:

NHibernate 会话都不是线程安全的,实体通常也不是线程安全的。事务用于解决并发问题,因此在单个事务中运行并行线程没有意义。

我不知道你用这些线程做什么。通常,您有一个用于单个事务的线程,您可以在其中运行任意数量的业务操作(一个接一个)。

您的事务需要将属于原子操作的所有内容放在一起。将原子操作拆分为多个事务不是一种选择。将多个独立的操作放入一个事务中只是为了重用会话缓存(这就是二级缓存的用途)也是没有意义的。

解决方案

1.不要在规则引擎中使用实体。我假设您有耗时的计算并希望将其拆分以使用多个处理器。在这种情况下,您只能在线程中进行计算。计算完成后,将结果返回给修改实体的事务线程。

2。不要使用延迟加载:关闭规则所需实体的延迟加载。这可以在查询中指定。

3.在主线程中加载实体值:这可能是最复杂的方式。编写一个在主线程中运行的服务。它将 lambda 表达式作为访问实体属性的参数。表达式在主线程中排队执行,结果返回给调用线程。

var myValue = entity.ExecuteInMainThread(x => x.Property);

我什至不能说这是否会过多地阻塞线程。顺便说一句,您可以使用 NHibernate 工具了解是否加载了集合或代理,因此您只需要在没有加载时返回主线程。当然,只有某些属性需要这样做,但为了保持一致,每个属性都应该以相同的方式访问。


我考虑这个问题的时间越长,我就越认为代理本身实现这样的功能会很好。代理需要启动一个在创建它的线程中运行的服务。然后它在该服务中排队延迟加载,而不是自己执行它。这对调用代码是完全透明的。

如果您要实现“线程安全延迟加载”,您可以将其实现为单独的代理实现。这使得它可以在不触及 NH 核心的情况下重复使用。这将是对 NH 的一个很好的贡献。

【讨论】:

  • 您说得非常正确,我需要同时处理一些耗时的计算。在这种情况下,您建议我通过单线程通道编组所有数据库更新?
  • 是的,像这样。理想情况下,计算有一些定义明确的参数,并且可以提供一些定义明确的变量的结果。然后你不需要对实体进行操作。您启动一个事务,读取计算所需的值,启动线程来运行它们,当它们完成时,您将结果存储回主线程中的实体并提交全部内容。
  • 嗯,参数是我持久保存到数据库的对象 - 规则引擎在执行时将其作为参数。这就是我在完成后更新的内容,以反映在规则执行期间其状态发生的变化。这对我来说似乎是一种更简洁的方式 - 但这意味着我不能共享我想要做的单个会话。
  • 不是小问题。我在答案中添加了一些我想到的解决方案。
  • stefan 的建议很棒,但您是否也考虑过您可能不需要延迟加载?如果非延迟加载的对象图不是很复杂和/或很重,最好一次性加载它,而不是对数据库进行大量往返
猜你喜欢
  • 2011-07-16
  • 1970-01-01
  • 1970-01-01
  • 2010-11-04
  • 2013-12-10
  • 2011-10-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多