问题
In the following code a deadlock is possible if two threads simultaneously invoke the transaction()
function, transposing different accounts.
void transaction(Account from, Account to, double amount)
{
mutex lock1, lock2;
lock1 = getlock(from);
lock2 = getlock(to);
acquire(lock1);
acquire(lock2);
withdraw(from, amount);
deposit(to, amount);
release(lock2);
release(lock1);
}
That is, one thread might invoke
transaction(checkingaccount, savingsaccount, 25);
and another might invoke
transaction(savingsaccount, checkingaccount, 50);
What is a good solution to this problem?
One I can think of is to use a witness program that will alert the user that a deadlock has occurred, but there must be a better solution implementable by modifying the code. Any ideas?
PS: This is from a textbook on operating systems. This is not homework, just part of the chapter on deadlocks.
回答1:
This is a problem described (along with the solution) in Java Concurrency in Practice, namely item 10.1.2 Dynamic lock order deadlocks, which is written specifically for Java but the logic can be well applied to other contexts (like yours).
So, as we cannot control in which order the arguments are going to be provided, we need to induce an ordering on the locks and acquire them according to the induced ordering consistently throughout the program we are writing.
One way to induce that order would be calculate the hash code of the from
and to
objects and synchronize first taking the lock from the object with lower hash code. In (the rare) case that two Account
objects have the same hash code, we would need to introduce a third lock which would "break" the tie.
For example in Java, it would be:
int fromHash = System.identityHashCode(from);
int toHash = System.identityHashCode(to);
Now, taking your code as reference, it could be something like the below code.
Object objectForTieBreakerLock = new Object(); // a valid new object here according to your language
void transaction(Account from, Account to, double amount)
{
mutex lock1, lock2, tieBreaker;
lock1 = getlock(from);
lock2 = getlock(to);
int fromHash = /*any language specific function to get object hash*/;
int toHash = /*any language specific function to get object hash*/;
if (fromHash < toHash) {
acquire(lock1);
acquire(lock2);
doTransaction(from, to, amount);
release(lock2);
release(lock1);
}
else if (fromHash > toHash) {
acquire(lock2);
acquire(lock1);
doTransaction(from, to, amount);
release(lock1);
release(lock2);
}
else {
tieBreaker = getlock(objectForTieBreakerLock);
acquire(tieBreaker);
acquire(lock1);
acquire(lock2);
doTransaction(from, to, amount);
release(lock2);
release(lock1);
release(tieBreaker);
}
}
// this must be a private (helper) method
void doTransaction(Account from, Account to, double amount)
{
withdraw(from, amount);
deposit(to, amount);
}
Additional notes
If
Account
would has a unique, immutable, comparable key such as a unique number, identifier, or something like that, inducing a lock ordering would be easier: order objects by their keys, eliminating this way the need for thetieBreaker
lock.Full Java Code Example: http://jcip.net/listings/InduceLockOrder.java
来源:https://stackoverflow.com/questions/29424306/solution-to-deadlock-lock-ordering