Solution to Deadlock: Lock Ordering

时光毁灭记忆、已成空白 提交于 2019-12-02 04:16:58

问题


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 the tieBreaker lock.

  • Full Java Code Example: http://jcip.net/listings/InduceLockOrder.java



来源:https://stackoverflow.com/questions/29424306/solution-to-deadlock-lock-ordering

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!