Free cookie consent management tool by TermsFeed The acquire() method of lock class in Python | Pythontic.com

The acquire() method of lock class in Python

Overview:

  • The acquire() method of the Lock class makes a Lock object into locked state.

  • A thread that wants to make other threads wait while it writes to a variable can do so by acquiring a Lock object before the write.

  • Once write is complete the thread can call release() on the Lock object which makes the state of the lock into unlocked state.

  • Any other thread that has to write using the same section of code has to wait on the same lock till it is acquired by using acquire(). As the previous thread calls the release(), the code writing to the variable becomes accessible to the other thread.

  • The acquire() method can be invoked on any of the following modes:

    • Blocking mode: The acquire() method can be invoked on blocking mode and the call blocks till the lock is unlocked by the owning thread. Once unlocked by a thread (usually the owning thread which in first place acquired the lock) if the current thread succeeds in acquiring the lock, the lock object is put into locked state and the acquire() method returns.

    • Block with timeout set: The acquire() can block only upto specified amount of time and return. Within the specified time the method can proceed to acquire the lock or the method can return without acquiring the lock upon the time elapse.

    • Non-blocking mode: The acquire() can also try to get the lock instantaneously and return regardless of the outcome of acquiring or not acquiring the lock.

Example:

The example program does not use a Lock object. Multiple threads increment a counter without having any synchronized access to it. When executing this program with the Python free threaded installation which is available in the Python version 3.1.13, it creates a race condition amongst the threads. As the critical resource is not synchronized with a Lock object, multiple threads just overwrite without any co-ordination resulting in an erroneous value for the counter.

Thread 1 has access to the resource while other threads wait:

Lock acquisition by thread 1

Thread 3 has access to the resource while other threads wait:

Lock acquisition by thread 3

Thread 2 has access to the resource while other threads wait:

Lock acquisition by thread 2

Modifying the program to increment the counter with a Python lock object results in the correct value for the counter. By uncommenting the with lock statement and bringing all the two lines inside the with block, makes use of the lock object and serializes the access to the counter err_no. As the lock is acquired prior to write to the err_no and released after the write to the err_no, the program accesses the err_no variable in a synchronized way thus preventing race conditions. The modified Python program is also available here.

# Example Python program that does not serialize
# access to a critical resource
import threading as th
import time
import sys

# The counter
err_num = 0

# Reader thread
def read(updateEvent, terminate):
    while(terminate.wait(5.0)):
        if(updateEvent.wait(5.0)):
            time.sleep(0.00001)
            print("Count value:%d"%err_num)

# Thread function for the increment threads
def incr(lock, updateEvent, thread_num):
    global err_num
    
    # Uncommenting the two lines below and increase
    # the intent of the next two lines will serialise
    # the access to the variable err_num
    #with lock:
    time.sleep(.01)
    err_num += 1

    updateEvent.set()
    print("Thread %d exiting"%thread_num)

# Print whether the Global Interpreter Lock(GIL) is enabled
print("GIL status:")
print(sys._is_gil_enabled())

# Create a lock
l = th.Lock()
threads = []

# Create events
countUpdate = th.Event()
countUpdate.clear()

threadsAlive = th.Event()
threadsAlive.clear()

# Create a reader thread
r = th.Thread(target = read, args = (countUpdate, threadsAlive))
r.start()

# Spawn writer threads
for i in range(0, 10):
    if i == 1:
        threadsAlive.set()
        print("At least one thread alive")
    t = th.Thread(target     = incr, 
                  args         = (l, countUpdate, i),)

    t.start()
    threads.append(t)

# Wait for the threads to complete
for t in threads:
    t.join()
countUpdate.clear()
threadsAlive.clear()
r.join()

# Print the final value of the counter
print("Final count:%d"%err_num)

Output:

Count value:1
Thread 2 exiting
Thread 8 exiting
Thread 5 exiting
Thread 3 exiting
Thread 1 exitingThread 4 exiting
Thread 6 exiting
Thread 9 exiting
Thread 7 exiting

Count value:4
Count value:4
Count value:4
Count value:4
Count value:4
Count value:4
Count value:4
Count value:4
Final count:4

 


Copyright 2025 © pythontic.com