您需要创建对象的总排序(在本例中为 Account 对象),然后根据该总排序始终以相同的顺序锁定它们。您可以决定将它们锁定在什么顺序中,但简单的事情是首先锁定总排序中排在首位的一个,然后再锁定另一个。
例如,假设每个帐户都有一个帐号,这是一个唯一的*整数。(* 表示没有两个帐户具有相同的号码)然后您总是可以先锁定具有较小帐号的那个。使用您的示例:
void transaction (Account from, Account to, double amount)
{
mutex first_lock, second_lock;
if (acct_no(from) < acct_no(to))
{
first_lock = get_lock(from);
second_lock = get_lock(to);
}
else
{
assert(acct_no(to) < acct_no(from)); // total ordering, so == is not possible!
assert(acct_no(to) != acct_no(from)); // this assert is essentially equivalent
first_lock = get_lock(to);
second_lock = get_lock(from);
}
acquire(first_lock);
acquire(second_lock);
withdraw(from, amount);
deposit(to, amount);
release(second_lock);
release(first_lock);
}
所以按照这个例子,如果 checks_account 有帐号。1 和 Savings_account 有帐号。2、transaction (savings_account, checking_account, 500);
会先锁定checking_account再锁定saving_account,transaction (checking_account, savings_account, 300);
也会先锁定checking_account再锁定savings_account。
如果您没有帐号(比如说您使用 Foo 类而不是 Account 类),那么您需要找到其他东西来建立总排序。如果每个对象都有一个名称,作为一个字符串,那么您可以进行字母比较以确定哪个字符串是“少”。或者,您可以使用任何其他与 > 和 < 可比较的类型。
但是,每个对象的值都是唯一的,这一点非常重要!如果两个对象在您正在测试的任何字段中具有相同的值,那么它们在排序中的相同位置。如果这可能发生,那么它是“部分排序”而不是“全排序”,并且对于这个锁定应用程序有一个全排序是很重要的。
如有必要,您可以组成一个“键值”,它是一个没有任何意义的任意数字,但保证该类型的每个对象都是唯一的。在创建每个对象时为其分配一个新的唯一值。
另一种选择是将所有该类型的对象保存在某种列表中。然后他们的列表位置用于将它们置于总排序中。(坦率地说,“键值”方法更好,但某些应用程序可能出于应用程序逻辑目的将对象保存在列表中,因此您可以在这种情况下利用现有列表。)但是,请注意不要结束当您使用这种方法时,需要花费 O(n) 时间(而不是像其他方法 * 那样 O(1))来确定总排序中哪个排在第一位。
(* 如果您使用字符串来确定总排序,那么它并不是真正的 O(1),但它与字符串的长度成线性关系,并且与包含这些字符串的对象的数量保持一致......但是,取决于在您的应用程序中,字符串长度可能比对象的数量更合理。)