Python simulation

In this section, we will implement the preceding situation in an actual Python program. Specifically, we will have two locks (we will call them lock A and lock B), and two separate threads interacting with the locks (thread A and thread B). In our program, we will set up a situation in which thread A has acquired lock A and is waiting to acquire lock B, which has already been acquired by thread B, which is, in turn, waiting for lock A to be released.

If you have already downloaded the code for this book from the GitHub page, go ahead and navigate to the Chapter12 folder. Let us consider the Chapter12/example1.py file, as follows:

# Chapter12/example1.py

import threading
import time

def thread_a():
print('Thread A is starting...')

print('Thread A waiting to acquire lock A.')
lock_a.acquire()
print('Thread A has acquired lock A, performing some calculation...')
time.sleep(2)

print('Thread A waiting to acquire lock B.')
lock_b.acquire()
print('Thread A has acquired lock B, performing some calculation...')
time.sleep(2)

print('Thread A releasing both locks.')
lock_a.release()
lock_b.release()

def thread_b():
print('Thread B is starting...')

print('Thread B waiting to acquire lock B.')
lock_b.acquire()
print('Thread B has acquired lock B, performing some calculation...')
time.sleep(5)

print('Thread B waiting to acquire lock A.')
lock_a.acquire()
print('Thread B has acquired lock A, performing some calculation...')
time.sleep(5)

print('Thread B releasing both locks.')
lock_b.release()
lock_a.release()

lock_a = threading.Lock()
lock_b = threading.Lock()

thread1 = threading.Thread(target=thread_a)
thread2 = threading.Thread(target=thread_b)

thread1.start()
thread2.start()

thread1.join()
thread2.join()

print('Finished.')

In this script, the thread_a() and thread_b() functions specify our thread A and thread B, respectively. In our main program, we also have two threading.Lock objects: lock A and lock B. The general structure of the thread instructions is as follows:

  1. Start the thread
  2. Try to acquire the lock with the same name as the thread (thread A will try to acquire lock A, and thread B will try to acquire lock B)
  3. Perform some calculations
  4. Try to acquire the other lock (thread A will try to acquire lock B, and thread B will try to acquire lock A)
  5. Perform some other calculations
  6. Release both locks
  7. End the thread

Note that we are using the time.sleep() function to simulate the action of some calculations being processed.

First of all, we are starting both thread A and thread B almost simultaneously, within the main program. With the structure of the thread instruction set in mind, we can see that at this point, both threads will be initiated; thread A will try to acquire lock A, and will succeed in doing so, since lock A is still available at this point. The same goes for thread B and lock B. The two threads will then go on to perform some calculations on their own.

Let us consider the current state of our program: lock A has been acquired by thread A, and lock B has been acquired by thread B. After their respective calculation processes are complete, thread A will then try to acquire lock B, and thread B will try to acquire lock A. We can easily see that this is the beginning of our deadlock situation: since lock B is already being held by thread B, and cannot be acquired by thread A, thread B, for the same reason, cannot acquire lock A.

Both of the threads will now wait infinitely, in order to acquire their respective second lock. However, the only way a lock can be released is for a thread to continue its execution instructions and release all of the locks it has at the end. Our program will therefore be stuck in its execution at this point, and no further progress will be made.

The following diagram further illustrates the process of how the deadlock unfolds, in sequence:

Deadlock sequence diagram

Now, let's see the deadlock that we have created in action. Run the script, and you should obtain the following output:

> python example1.py
Thread A is starting...
Thread A waiting to acquire lock A.
Thread B is starting...
Thread A has acquired lock A, performing some calculation...
Thread B waiting to acquire lock B.
Thread B has acquired lock B, performing some calculation...
Thread A waiting to acquire lock B.
Thread B waiting to acquire lock A.

As we discussed, since each thread is trying to acquire a lock that is currently held by the other thread, and the only way for a lock to be released is for a thread to continue its execution. This is a deadlock, and your program will hang infinitely, never reaching the final print statement in the last line of the program.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.17.81.201