这是涉及死锁的模拟银行数据库问题。我已经有了我认为的答案,但我很好奇这是否是一个很好的解决方案。提出的问题如下:
如何在以下代码中防止死锁?:
void transaction(Account from, Account to, double amount)
{
Semaphore lock1, lock2;
lock1 = getLock(from);
lock2 = getLock(to);
wait(lock1);
wait(lock2);
withdraw(from, amount);
deposit(to, amount);
signal(lock2);
signal(lock1);
}
这种方式可以变为死锁的方式是通过两个线程(或进程?)同时使用相反的帐户调用transaction()方法,即:
交易(储蓄,检查,1);在线程1和中 交易(检查,储蓄,2);在主题2中。从我糟糕的理解,我认为发生了什么以及它为何陷入僵局是因为没有遵守锁定顺序,因为两个线程都试图获得锁定(来自彼此?)。
我的快速而肮脏的解决方案是将函数transaction()之外的锁移动到调用它时的样子:
//somewhere in main
Semaphore lock1, lock2;
lock1 = getLock(from);
lock2 = getLock(to);
wait(lock1);
wait(lock2);
transaction(checking, savings, 2);
signal(lock2);
signal(lock1);
//.....
交易看起来像这样:
void transaction(Account from, Account to, double amount)
{
withdraw(from, amount);
deposit(to, amount);
}
这样他们就永远无法同时执行,因为交易在技术上是关键部分。如果这是一个java程序,你是否也可以通过在函数声明中将void一词置为void来使用监听器?那会有用吗?这似乎是一种更聪明的方法。
我也可能根本不理解这一点,所以请随意给我上学,特别是如果我的解释不准确的话。感谢。
答案 0 :(得分:5)
我认为发生了什么以及它为何陷入僵局是因为没有遵守锁定顺序,因为两个线程都试图获得锁定
这里的问题就像你提到的那样一个线程如:
wait(fromLock);
wait(toLock);
而另一个人可能会这样做:
wait(toLock);
wait(fromLock);
可能导致死锁。
您需要做的是确保他们始终以相同的顺序锁定。您可以以某种方式使用帐户的ID来确定锁的顺序:
if (from.id < to.id) {
wait(fromLock)
wait(toLock)
transaction(checking, savings, 2);
signal(toLock);
signal(fromLock);
} else {
wait(toLock)
wait(FromLock)
transaction(checking, savings, 2);
signal(FromLock);
signal(toLock);
}
我相信这将解决任何僵局。您可能还想检查from
和to
是否为同一实体。
答案 1 :(得分:2)
将这个关键部分分为两部分:
void transaction(Account from, Account to, double amount)
{
Semaphore lock1, lock2;
lock1 = getLock(from);
lock2 = getLock(to);
wait(lock1);
withdraw(from, amount);
signal(lock1);
wait(lock2);
deposit(to, amount);
signal(lock2);
}
将是现实世界中最好的解决方案,因为将这个关键部分分开并不会导致它们之间出现任何不需要的状态(也就是说,永远不会出现这种情况,金钱被取消,而不是可能)。
将关键部分保留为一个整体的简单解决方案是确保所有交易中的锁定顺序相同。 在这种情况下,您可以对锁进行排序,并选择较小的锁以便先锁定。
有关在这些情况下防止死锁的更多详细信息,请参阅我的问题 - Multiple mutex locking strategies and why libraries don't use address comparison
答案 2 :(得分:0)
在等待另一个锁时,任何事务都不应该保留一个锁。如果它无法获得所需的锁之一,那么它应该释放它已经获得的所有锁。