Running a code provided below, the lock object will acquire the lock only for the first time acquire() method is called but won’t acquire the lock the second time.
Why will such happen? This occurs because the normal lock object once acquired cannot be re-acquired even if the same threads attempt to do so.
But why would someone attempt calling the acquire() method twice since the lock won’t acquire the second time?
Below is a simple example to illustrate this simple locking issue:
lock = threading.Lock()
def get_first_line():
lock.acquire()
try:
# read some file and get the first line
finally:
lock.release()
return data
def get_second_line():
lock.acquire()
try:
# read the same file and get the second line
finally:
lock.release()
return data
There are presently two different functions reading different parts of data from a shared resource in the code above. A locking mechanism has been used to prevent any other thread from modifying the data of the file while the main thread is reading it.
Imagine calling both the functions one by one, these procedures should be followed:
first = get_first_line()
second = get_second_line()
return first, second
While data is being read from a specific or shared resource, its content can still be modified by any other thread in between the two function calls hence this call is not threaded safe.
To prevent the thread from being modified, a lock can be used before calling these two functions:
lock.acquire()
try:
first = get_first_line()
second = get_second_line()
finally:
lock.release()
return first, second
But this code won’t still work because acquire() will be called on the lock object inside the same thread trying to acquire the lock again inside the functions which are readily acquired before calling the functions.
Thus, in this tight situation, the primitive Lock object cannot be employed but an RLock will be used.
An RLock is an abbreviation for a re-entrant lock. A re-entrant lock can be acquired countless times by the same thread.
RLock object also has two important methods which they can call and they are:
Below is a simple example to demonstrate the working of the RLock object:
import threading
lock = threading.RLock()
print("Acquiring the lock: ", lock.acquire())
# adding 0 as timeout value else, the below statement will wait forever
print("Acquiring the lock again: ", lock.acquire())
Output:
Acquiring the lock: True
Acquiring the lock again: True
Also, the code in the simple locking issue example will also work without any issue if we employ the RLock object:
lock = threading.RLock()
lock.acquire()
try:
first = get_first_line()
second = get_second_line()
finally:
lock.release()
return first, second
The above code will work perfectly.