NHibernate并发问题

5
我有一个使用S#arp Architecture的应用程序,实现了一个轻量级队列处理机制,各个线程从列表中拉取实体并设置它们的状态,以标记已经开始处理这些项。

尽管在显式事务中包装了启动处理部分并使用了C# lock(),但有时仍会同时启动它们。

我后悔没有使用MSMQ……嗯,但现在这种并发行为让我困惑了。显然,我对NHibernate事务和刷新机制有所不理解。你能帮我解决问题吗?

以下是相关代码:

private static object m_lock = new object();

private bool AbleToStartProcessing(int thingId)
{
    bool able = false;
    try
    {
        lock (m_lock)
        {
            this.thingRepository.DbContext.BeginTransaction();
            var thing = this.thingRepository.Get(thingId);
            if (thing.Status == ThingStatusEnum.PreProcessing)
            {
                able = true;
                thing.Status = ThingStatusEnum.Processing;
            }
            else
            {
                logger.DebugFormat("Not able to start processing {0} because status is {1}",
                        thingId, thing.Status.ToString());
            }
            this.thingRepository.DbContext.CommitTransaction();
        }
    }
    catch (Exception ex)
    {
        this.thingRepository.DbContext.RollbackTransaction();
        throw ex;
    }
    if (able)
        logger.DebugFormat("Starting processing of {0}",
                        thingId);
    return able;
}

我本希望这种方法可以保证同一时间只有一个线程更改“事物”的状态,但是日志中经常出现以下情况:

2011-05-18 18:41:23,557 thread41 DEBUG src:MyApp.Blah.ThingJob - Starting processing of 78090
2011-05-18 18:41:23,557 thread51 DEBUG src:MyApp.Blah.ThingJob - Starting processing of 78090

两个线程尝试对同一个东西进行操作,从而导致混乱。

我错过了什么吗?谢谢。

编辑:更改代码以反映实际版本中的日志记录方式。


你能解决这个问题吗? - Rex Morgan
我还没有机会尝试你的建议,但我很快会去尝试。 - codeulike
2个回答

1

哇,这是否意味着除非在某处设置并发选项,否则所有的Nhibernate事务代码都没有任何作用? - codeulike
我不确定,但如果您不想设置映射,可以尝试这个。session.Lock(thing, LockMode.Upgrade); - Rex Morgan

0

我认为你只是在使用设置正在处理和检查是否已经处理的状态时出现了混淆。第一个人设置ThingStatusEnum.Processing,但下一个人却检查了另一种不同的状态——ThingStatusEnum.PreProcessing。因为ThingStatusEnum.Processing!= ThingStatusEnum.PreProcessing,所以你的锁定意味着两个线程都没有……


但是我不明白,即使某个地方存在逻辑问题,如果记录器从临界区域内部记录日志,它如何报告相同的时间。可能是记录器本身不准确(Log4net?)。也许尝试将时间附加到日志中,看看它们是否实际上会发生冲突,这似乎不太可能。 - Ernesto
之前没有注意到时间完全相同。你是对的,这表明它正在同时运行。使用静态锁对象不应该是可能的。这与nhibernate或s#arp架构无关。多个线程不应该能够同时运行该代码,无论是否涉及任何数据库交互。如果锁对象不是静态的,我会认为你正在处理不同的实例,但是我目前无法回答你的问题。我仍然认为状态检查存在缺陷,但我无法解释日志中的时间。 - Dave Rael
@Ernesto,@Dave - 哎呀,抱歉,我在发布代码时进行了“简化”,并移动了日志语句。在我的版本中,记录日志发生在锁之外 - 你们完全正确,同时两个日志是不可能的。我会编辑问题。 - codeulike
不认为这是逻辑问题——项目从预处理状态开始,当它们被拾取时会变成处理状态。代码摘录的目的是确保在任何时候只有一个线程可以进行该更改。 - codeulike
@codeulike - 我知道这是很久以前的事情了,但我回顾了一些旧的东西,发现这个问题仍然没有答案,看起来我在你更新后没有重新访问。根据你的更新,我认为你只是一次处理一个。在锁定外部记录时,您没有限制同时记录,但实际上只有一个事务正在进行。看起来你可能认为锁会阻止第二个事务的发生 - 如果你这样认为,那就是一个误解 - 它只是等待第一个完成,然后继续进行。 - Dave Rael

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接