Examples of deadlock formation in Python and the prevention of deadlock conditions

Source: Internet
Author: User
Deadlock Example
Multi-threading often encounter deadlock problems, learning the operating system will talk about the deadlock related things, we use Python visual demonstration.
One reason for a deadlock is a mutex. Assuming a banking system, user A tries to transfer 100 dollars to User B, while User B tries to transfer 200 to user A, which may result in a deadlock.
2 threads wait for each other's locks, occupying resources without releasing each other.

#coding =utf-8 Import time Import threading class account:   def __init__ (self, _id, balance, lock):     self.id = _id
  self.balance = Balance     Self.lock = lock    def withdraw (self, amount):     self.balance-= Amount    def Deposit (self, amount):     self.balance + amount   def transfer (_from, to, amount):   if _from.lock.acquire (): # Lock your account     _from.withdraw (amount)     time.sleep (1) #让交易时间变长, 2 trading threads overlap on time, there is enough time to generate deadlock     print ' Wait for lock ... '     if To.lock.acquire (): #锁住对方的账户       to.deposit (amount)       to.lock.release ()     _from.lock.release ( )   print ' Finish ... '  

locking mechanism to prevent deadlocks
problem:
You are writing a multithreaded program in which threads need to acquire multiple locks at once, and how to avoid deadlock problems.
Solution:
In multithreaded programs, a large part of the deadlock problem is caused by threads acquiring multiple locks at the same time. For example, when a thread acquires the first lock and then blocks when it acquires a second lock, the thread can block the execution of other threads, causing the entire program to feign death. One solution to the deadlock problem is to assign a unique ID to each lock in the program, and then allow only the use of multiple locks in ascending rules, which is very easy to implement using the context manager, as shown in the following example:

Import threadingfrom contextlib Import contextmanager# thread-local state to stored information on locks already acquired_ Local = threading.local () @contextmanagerdef Acquire (*locks):  # Sort Locks by object identifier  locks = sorted ( Locks, Key=lambda X:id (x))  # Make sure lock order of previously acquired locks was not violated  acquired = GetAttr (_local, ' acquired ', [])  If acquired and Max (ID (lock) for lock in acquired) >= ID (locks[0]):    raise RuntimeError (' Lock Order violation ')  # Acquire all of the locks  acquired.extend (locks)  _local.acquired = acquired  try: for-    lock in locks:< C11/>lock.acquire ()    yield  finally:    # Release Locks in reverse order of acquisition for    Lock in Reversed (locks):      lock.release ()    del Acquired[-len (locks):]

How do I use this context manager? You can create a lock object in a normal way, but the acquire () function is used to request a lock in either a single lock or multiple locks, as shown in the following example:

Import Threadingx_lock = Threading. Lock () Y_lock = Threading. Lock () def thread_1 (): While  True:    with Acquire (X_lock, y_lock):      print (' Thread-1 ') def thread_2 ():  While True:    with Acquire (Y_lock, x_lock):      print (' Thread-2 ') T1 = threading. Thread (target=thread_1) T1.daemon = Truet1.start () t2 = Threading. Thread (target=thread_2) T2.daemon = Truet2.start ()

If you execute this code, you will find that it does not have a deadlock even if it acquires a lock in a different order in different functions. The key is that in the first piece of code, we sort the locks. By sorting, the locks are retrieved in a fixed order, regardless of the order in which they are requested. If more than one acquire () operation is nested, a potential deadlock problem can be detected through thread local storage (TLS). Let's say your code is written like this:

Import Threadingx_lock = Threading. Lock () Y_lock = Threading. Lock () def thread_1 (): While  True: with    Acquire (X_lock): with      Acquire (y_lock):        print (' Thread-1 ') def Thread_2 (): While  True: with    Acquire (Y_lock): with      Acquire (x_lock):        print (' Thread-2 ') T1 = threading . Thread (target=thread_1) T1.daemon = Truet1.start () t2 = Threading. Thread (target=thread_2) T2.daemon = Truet2.start ()

If you run this version of the code, there must be a thread crash, and the exception information might look like this:

Exception in Thread Thread-1:traceback (most recent call last): File "/usr/local/lib/python3.3/threading.py", line 639, in _bootstrap_inner  Self.run () File "/usr/local/lib/python3.3/threading.py", line 596, in Run  self._target (* Self._args, **self._kwargs) file "deadlock.py", line, in Thread_1 with  acquire (y_lock): File "/usr/local/lib/ python3.3/contextlib.py ", line $, in __enter__  return next (Self.gen) File ' deadlock.py ', line ' in acquire  Rai Se runtimeerror ("Lock Order violation") Runtimeerror:lock order violation>>>

The reason for the crash is that each thread records the locks that it has acquired. The Acquire () function examines the list of locks that were previously acquired, because the locks are obtained in ascending order, so the function will assume that the ID of the previously acquired lock must be less than the newly requested lock, and the exception will be triggered.

Discuss
Deadlocks are a problem for every multithreaded operation (as it is a common topic for every operating-system textbook). As a matter of experience, it is possible to ensure that each thread can hold only one lock at a time, so that the program is not plagued by deadlock problems. Once the thread has applied for multiple locks at the same time, everything is unpredictable.

Deadlock detection and recovery is an extended topic with virtually no elegant solutions. A more common deadlock detection and recovery scheme is the introduction of watchdog counters. When the thread is running normally, it resets the counter every once in a while, without a deadlock, everything works. Once a deadlock occurs, the timer expires because the counter cannot be reset, and the program resumes itself to a normal state by rebooting itself.

Avoiding deadlocks is another way to resolve deadlock problems, which are obtained in ascending order of the object ID when the process acquires the lock, and are mathematically proven so that the program does not enter a deadlock state. The proof is left to the reader as an exercise. The main idea of avoiding deadlocks is that simply adding locks in ascending order of object IDs does not produce circular dependencies, and cyclic dependencies are a necessary condition for deadlocks, which prevents the program from entering a deadlock state.

The following is a classic question about thread deadlock: "The question of dining philosophers" as the last example of this section. The question is this: five philosophers sit around a table, with a bowl of rice and a chopstick in front of each person. Here each philosopher can be regarded as a separate thread, and each chopstick can be regarded as a lock. Each philosopher can sit, think, and eat in one of three states. It is important to note that every philosopher eats two chopsticks, so the question comes: if every philosopher picks up his left chopsticks, then all five of them can only sit with one chopstick until they starve to death. At this point they entered the deadlock state. The following is a simple use of deadlock avoidance mechanism to solve the "philosopher dining problem" implementation:

Import threading# the philosopher Threaddef philosopher (left, right): and  True: with    Acquire (left,right):       print (Threading.currentthread (), ' eating ') # The chopsticks (represented by locks) nsticks = 5chopsticks = [threading . Lock () for n a range (nsticks)]# Create All of the philosophersfor N in range (nsticks):  t = Threading. Thread (Target=philosopher,             args= (chopsticks[n],chopsticks[(n+1)% nsticks]))  T.start ()

Finally, it is important to note that in order to avoid deadlocks, all lock operations must use the acquire () function. If a part of the code bypasses the acquire function to request a lock directly, then the entire deadlock avoidance mechanism will not work.

  • Related Article

    Contact Us

    The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

    If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.