【发布时间】:2015-07-20 18:34:08
【问题描述】:
我在多线程场景中使用 NHibernate。这是一个执行以下操作的测试。
- (启动多线程服务器。)
- 向服务器发送请求。服务器创建一个状态为
A的新Foo对象,并使用NHibernate 将其存储在数据库中。 - 为新创建的
Foo对象设置状态B。 - 向服务器发送请求。服务器使用 NHibernate 从 DB 获取
Foo,读取其状态(应为B)并采取相应措施。 - (停止服务器。)
现在我的问题是,在第 4 步中偶尔(在 1 - 2% 的情况下)服务器会读取状态 A,而它应该读取 B 并且测试失败。 我在这里做错了什么?
NHibernate 会话通过设置为 thread static 的CurrentSessionContext 检索。我使用 Fluent NHibernate,但我想这并不重要。
编辑
关于服务器的详细信息:
服务器是一个简单的 TCP 服务器。启动时,它会创建一个新的 thread task 来等待传入的连接。当一个新连接被接受时,它会创建另一个 thread task 来处理来自连接的请求并发送响应。处理thread task 终止。
服务器使用Task.Factory.StartNew() 创建新的线程 任务。
编辑 2
我编写了一个小型 NUnit 测试来重现此行为。它失败了
预期:打开但被:初始化
在第一次迭代中。
[Test]
[Explicit]
public void NHibernateProblem()
{
for (var i = 0; i < 100; i++)
{
var identifier = Guid.NewGuid().ToString();
var session1 = Database.GetCurrentSession();
using (var dbTransaction = session1.BeginTransaction())
{
var transaction1 = new Transaction
{
Identifier = identifier,
Status = TransactionStatus.Initialized
};
session1.SaveOrUpdate(transaction1);
dbTransaction.Commit();
}
Task.Factory.StartNew(() =>
{
var session2 = Database.GetCurrentSession();
using (var dbTransaction = session2.BeginTransaction())
{
var transaction2 = session2
.QueryOver<Transaction>()
.Where(t => t.Identifier == identifier)
.SingleOrDefault();
transaction2.Status = TransactionStatus.Open;
session2.SaveOrUpdate(transaction2);
dbTransaction.Commit();
}
}).Wait();
var session3 = Database.GetCurrentSession();
using (var dbTransaction = session3.BeginTransaction())
{
var transaction3 = session3
.QueryOver<Transaction>()
.Where(t => t.Identifier == identifier)
.SingleOrDefault();
dbTransaction.Commit();
Console.WriteLine("iteration {0}", i);
Assert.That(transaction3.Status, Is.EqualTo(TransactionStatus.Open));
}
}
}
Database.GetCurrentSession() 看起来像这样:
public virtual ISession GetCurrentSession()
{
if (CurrentSessionContext.HasBind(SessionFactory))
{
var currentSession = SessionFactory.GetCurrentSession();
return currentSession;
}
var session = SessionFactory.OpenSession();
session.FlushMode = FlushMode.Always;
session.CacheMode = CacheMode.Ignore;
CurrentSessionContext.Bind(session);
return session;
}
我认为映射关系不大。
【问题讨论】:
-
我认为你必须更详细地展示你正在做的事情,以便任何人弄清楚它有什么问题。
-
另外,你说的是“多线程”,但不清楚这些多线程在哪里或如何涉及。
-
@OskarBerggren 我试图描述它是因为很难将整个应用程序简化为几行代码。我还提到了多线程服务器,因为我认为它与会话或状态的同步有关。
-
嗯,这里很可能存在某种竞争条件,但从如此高级别的描述中找不到。您同时有多少个请求?即使所有请求都按顺序而不是同时运行,您是否看到问题?您如何管理会话(生命周期)?缓存?等等……
-
查看我的测试。我希望这比我的高级描述更好。
标签: c# multithreading nhibernate fluent-nhibernate