【问题标题】:Nhibernate Concurrency between multiple session多个会话之间的休眠并发
【发布时间】:2017-04-10 22:45:11
【问题描述】:

我有一个表格,其中包含一列预先填充的数字。我使用 Nhibernate 的 API 抓取了前 10 行,其中“已使用”标志设置为 false。 当多个会话尝试从表中抓取行时,避免并发问题的最佳方法是什么? 选择行后,我可以将标志列更新为 True,以便后续调用不会使用相同的数字。

【问题讨论】:

    标签: sql-server nhibernate concurrency locking fluent-nhibernate


    【解决方案1】:

    在这样一个大背景下,可以这样做:

    // RepeatableRead ensures the read rows does not get concurrently updated by another
    // session.
    using (var tran = session.BeginTransaction(IsolationLevel.RepeatableRead))
    {
        var entities = session.Query<Entity>()
            .Where(e => !e.Used)
            .OrderBy(e => e.Id)
            .Take(10)
            .ToList();
        foreach(var entity in entities)
        {
            e.Used = true;
        }
        // If your session flush mode is not the default one and does not cause
        // commits to flush the session, add a session.Flush(); call before committing.
        tran.Commit();
        return entities;
    }
    

    这很简单。它可能会因死锁而失败,在这种情况下,您必须丢弃会话,获取一个新会话,然后重试。

    使用乐观更新模式可能是另一种解决方案,但这也需要一些代码来从失败的尝试中恢复。

    使用不会导致死锁风险的无显式锁定解决方案可以做到,但需要更多查询:

    const int entitiesToObtain = 10;
    // Could initialize here with null instead, but then, will have to check
    // for null after the while too.
    var obtainedEntities = new List<Entity>();
    while (obtainedEntities.Count == 0)
    {
        List<Entity> candidates;
        using (var tran = session.BeginTransaction())
        {
            candidatesIds = session.Query<Entity>()
                .Where(e => !e.Used)
                .Select(e => e.Id)
                .OrderBy(id => id)
                .Take(entitiesToObtain)
                .ToArray();
        }
        if (candidatesIds.Count == 0)
            // No available entities.
            break;
    
        using (var tran = session.BeginTransaction())
        {
            var updatedCount = session.CreateQuery(
                @"update Entity e set e.Used = true
                    where e.Used = false
                        and e.Id in (:ids)")
                .SetParameterList("ids", candidatesIds)
                .ExecuteUpdate();
            if (updatedCount == candidatesIds.Length)
            {
                // All good, get them.
                obtainedEntities = session.Query<Entity>()
                    .Where(e => candidatesIds.Contains(e.Id))
                    .ToList();
                tran.Commit();
            }
            else
            {
                // Some or all of them were no more available, and there
                // are no reliable way to know which ones, so just try again.
                tran.Rollback();
            }
        }
    }
    

    这使用NHibernate DML-style operations 建议herestrongly typed alternative 在 NHibernate v5.0 中可用。

    【讨论】:

      猜你喜欢
      • 2014-11-17
      • 2014-04-23
      • 1970-01-01
      • 1970-01-01
      • 2020-10-17
      • 2015-10-03
      • 1970-01-01
      • 2012-06-02
      相关资源
      最近更新 更多