-- Back to Index --
Livelock
is a concurrency issue where two or more proceess or threads are actively responding to each other's action but are unable to make progress toward completing their tasks. Unlike a Deadlock
, where process are blocked and waiting indefinitely.
Let's do this In Perl
first.
#!/usr/bin/env perl
use v5.38;
use threads;
use threads::shared;
use Time::HiRes qw(sleep);
my $lock_a :shared = 1;
my $lock_b :shared = 1;
my $lock_c :shared = 1;
my $counter :shared = 9;
sub try_lock($this) {
{
lock($$this);
return 1 if $$this;
}
sleep(rand() / 10);
return 0;
}
sub worker($name, $first, $second) {
while ($counter > 0) {
{
lock($$first);
if ($$first && try_lock($second)) {
if ($counter > 0) {
$counter--;
say "$name took the turn, counter is $counter now.";
}
}
}
sleep(rand() / 10);
}
}
my @threads;
push @threads, threads->create(\&worker, 'Thread 1', \$lock_a, \$lock_b);
push @threads, threads->create(\&worker, 'Thread 2', \$lock_b, \$lock_c);
push @threads, threads->create(\&worker, 'Thread 3', \$lock_c, \$lock_a);
$_->join foreach @threads;
NOTE: threads->create()
creates a new thread and immediately start executing the worker routine. $_->join
makes the main process to wait until the thread finished executing the worker.
See the action now:
$ perl livelock.pl
Thread 1 took the turn, counter is 8 now.
Thread 2 took the turn, counter is 7 now.
Thread 3 took the turn, counter is 6 now.
Thread 2 took the turn, counter is 5 now.
Thread 3 took the turn, counter is 4 now.
Thread 2 took the turn, counter is 3 now.
Thread 1 took the turn, counter is 2 now.
Thread 2 took the turn, counter is 1 now.
Thread 3 took the turn, counter is 0 now.
$
Time to do the same in Python
:
#!/usr/bin/env python3
import time
import threading
from random import random
lock_a = threading.Lock()
lock_b = threading.Lock()
lock_c = threading.Lock()
counter = 9
def worker(name, first, second):
global counter
while counter > 0:
first.acquire()
if not second.acquire(blocking=False):
first.release()
time.sleep(random()/10)
else:
try:
if counter > 0:
counter -= 1
print(f"{name} took the turn, counter is {counter} now.")
finally:
second.release()
first.release()
if __name__ == '__main__':
threads = []
threads.append(threading.Thread(target=worker, args=('Thread 1', lock_a, lock_b)))
threads.append(threading.Thread(target=worker, args=('Thread 2', lock_b, lock_c)))
threads.append(threading.Thread(target=worker, args=('Thread 3', lock_c, lock_a)))
for thread in threads:
thread.start()
for thread in threads:
thread.join()
NOTE: thread.start()
starts executing the worker method in a separate thread. thread.join()
makes the calling process to wait until the thread is done executing the worker.
Run the code as below:
$ py livelock.py
Thread 1 took the turn, counter is 8 now.
Thread 2 took the turn, counter is 7 now.
Thread 2 took the turn, counter is 6 now.
Thread 2 took the turn, counter is 5 now.
Thread 2 took the turn, counter is 4 now.
Thread 2 took the turn, counter is 3 now.
Thread 2 took the turn, counter is 2 now.
Thread 2 took the turn, counter is 1 now.
Thread 2 took the turn, counter is 0 now.
$
-- Back to Index --