Understanding Thread Deadlock in Python

Learn about thread deadlock in Python, a concurrency issue where threads become stuck waiting for conditions that never occur, leading to a frozen program. Discover common causes of deadlock, including issues with mutex locks, inter-thread dependencies, and improper resource management.



Thread Deadlock in Python

A deadlock is a concurrency failure mode where one or more threads wait for a condition that never occurs. This situation causes the threads to be unable to progress, making the program stuck or frozen.

Common Causes of Thread Deadlocks

  • A thread attempts to acquire the same mutex lock twice.
  • Threads wait on each other (e.g., A waits on B, B waits on A).
  • A thread fails to release a resource such as a lock, semaphore, condition, or event.
  • Threads acquire mutex locks in different orders (fail to perform lock ordering).

To prevent data inconsistency, concurrent handling should be synchronized so that resources are locked when one thread is using them.

The Lock Object

The Lock class in the threading module allows you to synchronize threads. A new lock is created by calling the Lock() method, which returns the new lock object.

The acquire() Method

This method changes the state to locked and returns immediately if the state is unlocked. It takes an optional blocking argument.

Syntax

Lock.acquire(blocking, timeout)

The release() Method

This method changes the state to unlocked when called in the locked state. It can be called from any thread, not just the one that acquired the lock.

Syntax

Lock.release()

Example

In the following program, two threads try to call the synchronized() method. One thread acquires the lock and gains access, while the other waits. Once the first thread completes, the lock is released and available for the second thread.

Example

from threading import Thread, Lock
import time

lock = Lock()
threads = []

class myThread(Thread):
    def __init__(self, name):
        Thread.__init__(self)
        self.name = name

    def run(self):
        lock.acquire()
        synchronized(self.name)
        lock.release()

def synchronized(threadName):
    print(f"{threadName} has acquired lock and is running synchronized method")
    counter = 5
    while counter:
        print('**', end='')
        time.sleep(2)
        counter -= 1
    print('\nlock released for', threadName)

t1 = myThread('Thread1')
t2 = myThread('Thread2')

t1.start()
threads.append(t1)

t2.start()
threads.append(t2)

for t in threads:
    t.join()

print("end of main thread")
Output

Thread1 has acquired lock and is running synchronized method
**********
lock released for Thread1
Thread2 has acquired lock and is running synchronized method
**********
lock released for Thread2
end of main thread

The Semaphore Object

Python supports thread synchronization with a semaphore. A semaphore uses an internal counter, which is decremented by each acquire() call and incremented by each release() call. The counter can never go below zero. When it is zero, the acquire() method blocks until a release() method is called.

The acquire() Method

If the internal counter is larger than zero, it is decremented by one and returns True immediately. If zero, it blocks until a release() call increments the counter above zero.

The release() Method

This method releases a semaphore, incrementing the internal counter by one. If other threads are waiting for the semaphore to become larger than zero, they are awoken.

Example

Example

from threading import Semaphore, Thread
import time

# creating thread instance where count = 3
lock = Semaphore(4)

def synchronized(name):
    lock.acquire()
    for n in range(3):
        print('Hello! ', end='')
        time.sleep(1)
        print(name)
    lock.release()

# creating multiple threads
thread_1 = Thread(target=synchronized, args=('Thread 1',))
thread_2 = Thread(target=synchronized, args=('Thread 2',))
thread_3 = Thread(target=synchronized, args=('Thread 3',))

# starting the threads
thread_1.start()
thread_2.start()
thread_3.start()
Output

Hello! Hello! Hello! Thread 1
Hello! Thread 2
Thread 3
Hello! Hello! Thread 1
Hello! Thread 3
Thread 2
Hello! Hello! Thread 1
Thread 3
Thread 2