【问题标题】:Locking multiple objects simultaneously同时锁定多个对象
【发布时间】:2016-05-18 15:51:01
【问题描述】:

在我的代码中,我有一种方法可以将资金从一个帐户转移到另一个帐户。显然,这种方法必须确保在程序执行的任何给定时刻,每个帐户只执行一个事务。我已经使用 2 个嵌套锁实现了这个检查(Account 是一个具有唯一整数 ID 的数据库实体):

public void Transfer(Account from, Account to, decimal amount)
{
    lock(GetLockObject(from))
    {
        lock(GetLockObject(to))
        {
            DoTransfer(from, to, amount);
        }
    }
}

private static Dictionary<int, object> lockObjects = new Dictionary<int, object>();
private static GetLockObject(Accont acc)
{
    lock (lockObjects)
    {
        var lockObject = lockObjects[acc.Id];
        if (lockObject == null)
        {
            lockObject = new object();
            lockObjects[accId] = lockObject;
        }
        return lockObject;
    }
}

这似乎工作正常,但我有两个问题:

  • 如果使用相同的 2 个帐户作为其参数同时调用该方法,则代码可能会导致死锁:Transfer(acc1, acc2, 10);Transfer(acc2, acc1, 10); 如何避免死锁?
  • 能否以比使用嵌套锁更好的方式(在性能、可靠性等方面)实现相同的结果?

【问题讨论】:

    标签: c# concurrency locking


    【解决方案1】:

    关于您的第一点:介绍一些帐户顺序(例如通过 id),首先锁定具有较小 id 的帐户,其次锁定具有较大 id 的帐户。

    至于你的第二个问题,有选项。例如,如果您确定需要锁定to 帐户,并且当balance(from) + balance(to) 小于输入Transfer 方法时(小于amount)时,您可以容忍一个小的不一致窗口,那么你可以这样做:

    public void Transfer(Account from, Account to, decimal amount) {
        bool withdrawalSuccessful = false;
        lock(GetLockObject(from)) {
            withdrawalSuccessful = Withdraw(from, amount);
        }
        if (withdrawalSuccessful) {
            lock(GetLockObject(to)) {
                Deposit(to, amount)
            }
        }
    }
    

    或者您可以完全摆脱锁定“to”:与其将账户余额表示为一个数字,不如存储“存款”和“取款”的集合(无论是普通列表还是数据库表中的记录或其他任何内容) )。然后你可能只是将存款添加到“到”而不锁定。

    或者让它不是“存款”和“取款”,而只是一些金额以及所有者信息和一些ID。然后,要进行转账,您:

    1. 锁定“来自”
    2. 检查属于from 的元组的货币总和是&gt;= amount
    3. 拆分它的一些元组,比如(id0, M, from),其中M &gt;= amount(如果没有这样的元组,则先合并一些现有的元组)到[(id1, amount, from), (id2, M-amount, from)],然后原子地更改(id1, amount, from) -&gt; (id1, amount, to)"UPDATE money_tuples SET owner='&lt;to&gt;' WHERE id='&lt;id1&gt;'"或只是"tuple.owner = to;"),
    4. 终于解锁“来自”。

    与上一个选项的不同之处在于您使用(“原子”)“更新”或变量赋值,而不是将两个对象添加到单独的集合中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多