The concept of livelock

The concept of livelock is connected to deadlock; some even consider it an alternate version of deadlock. In a livelock situation, the processes (or threads) in the concurrent program are able to switch their states; in fact, they switch states constantly. Yet, they simply switch back and forth infinitely, and no progress is made. We will now consider an actual scenario of livelock.

Suppose that a pair of spouses are eating dinner together at a table. They only have one fork to share with each other, so only one of them can eat at any given point. Additionally, the spouses are really polite to each other, so even if one spouse is hungry and wants to eat their food, they will leave the fork on the table if their partner is also hungry. This specification is at the heart of creating a livelock for this problem: when both spouses are hungry, each will wait for the other to eat first, creating a infinite loop in which each spouse switches between wanting to eat and waiting for the other spouse to eat.

Let's simulate this problem in Python. Navigate to Chapter12/example8.py, and take a look at the Spouse class:

# Chapter12/example8.py

class Spouse(threading.Thread):

def __init__(self, name, partner):
threading.Thread.__init__(self)
self.name = name
self.partner = partner
self.hungry = True

def run(self):
while self.hungry:
print('%s is hungry and wants to eat.' % self.name)

if self.partner.hungry:
print('%s is waiting for their partner to eat first...'
% self.name)
else:
with fork:
print('%s has stared eating.' % self.name)
time.sleep(5)

print('%s is now full.' % self.name)
self.hungry = False

This class inherits from the threading.Thread class and implements the logic that we discussed previously. It takes in a name for the Spouse instance and another Spouse object as its partner; when initialized, a Spouse object is also always hungry (the hungry attribute is always set to True). The run() function in the class specifies the logic when the thread is started: as long as the Spouse object's hungry attribute is set to True, the object will attempt to use the fork, which is a lock object, to eat. However, it always checks to see whether its partner also has its hungry attribute set to True, in which case, it will not proceed to acquire the lock, and will instead wait for its partner to do it.

In our main program, we create the fork as a lock object first; then, we create two Spouse thread objects, which are each other's partner attributes. Finally, we start both threads, and run the program until both threads finish executing:

# Chapter12/example8.py

fork = threading.Lock()

partner1 = Spouse('Wife', None)
partner2 = Spouse('Husband', partner1)
partner1.partner = partner2

partner1.start()
partner2.start()

partner1.join()
partner2.join()

print('Finished.')

Run the script, and you will see that, as we discussed, each thread will go into an infinite loop, switching between wanting to eat and waiting for its partner to eat; the program will run forever, until Python is interrupted. The following code shows the first few lines of the output that I obtained:

> python3 example8.py
Wife is hungry and wants to eat.
Wife is waiting for their partner to eat first...
Husband is hungry and wants to eat.
Wife is hungry and wants to eat.
Husband is waiting for their partner to eat first...
Wife is waiting for their partner to eat first...
Husband is hungry and wants to eat.
Wife is hungry and wants to eat.
Husband is waiting for their partner to eat first...
Wife is waiting for their partner to eat first...
Husband is hungry and wants to eat.
Wife is hungry and wants to eat.
Husband is waiting for their partner to eat first...
...
..................Content has been hidden....................

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