Example of deadlock handling

Let's look at a quick example in Python. Let's a take look at the Chapter04/example2.py file, as shown in the following code:

# Chapter04/example2.py

from threading import Lock

my_lock = Lock()

def get_data_from_file_v1(filename):
my_lock.acquire()

with open(filename, 'r') as f:
data.append(f.read())

my_lock.release()

data = []

try:
get_data_from_file('output2/sample0.txt')
except FileNotFoundError:
print('Encountered an exception...')

my_lock.acquire()
print('Lock can still be acquired.')

In this example, we have a get_data_from_file_v1()function that takes in the path to an external file, reads the data from it, and appends that data to a predeclared list called data. Inside this function, a lock object called my_lock, which is also predeclared prior to the function being called, is acquired and released as the parameter file is read before and after, respectively.

In the main program, we will try to call get_data_from_file_v1() on a nonexistent file, which is one of the most common errors in programming. At the end of the program, we also acquire the lock object again. The point is to see whether our programming could handle the error of reading a nonexistent file appropriately and gracefully with just the try...except block that we have.

After running the script, you will notice that our program will print out the error message specified in the try...except block, Encountered an exception..., which is expected, since the file could not be found. However, the program will also fail to execute the rest of the code; it will never get to the last line of code—print('Lock acquired.')—and will hang forever (or until you hit Ctrl + C to force-quit the program).

This is a deadlock situation, which, again, occurs when my_lock is acquired inside the get_data_from_file_v1() function, but since our program encountered an error before executing my_lock.release(), the lock was never released. This in turn caused the my_lock.acquire() line at the end of the program to hang, as the lock could not be acquired in any way. Our program hence could not reach its last line of code, print('Lock acquired.').

This problem, however, could be handled with a with statement easily and effortlessly. In the example2.py file, simply comment out the line calling get_data_from_file_v1() and uncomment the line calling get_data_from_file_v2(), and you will have the following:

# Chapter04/example2.py

from threading import Lock

my_lock = Lock()

def get_data_from_file_v2(filename):
with my_lock, open(filename, 'r') as f:
data.append(f.read())

data = []

try:
get_data_from_file_v2('output2/sample0.txt')
except:
print('Encountered an exception...')

my_lock.acquire()
print('Lock acquired.')

In the get_data_from_file_v2() function, we have the equivalent of a pair of nested with statements, as follows:

with my_lock:
with open(filename, 'r') as f:
data.append(f.read())

Since Lock objects are context managers, simply using with my_lock: would ensure that the lock object is acquired and released appropriately, even if an exception is encountered inside the block. After running the script, you will have the following output:

> python example2.py
Encountered an exception...
Lock acquired.

We can see that, this time, our program was able to acquire the lock and reach the end of the script gracefully and without errors.

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

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