To handle this complexity, the threading module provides some synchronization primitives, such as locks, joins, semaphores, events, and condition variables.
9.02_lock_demo.py slightly modifies the preceding code by introducing a lock using this line:
self.lock = threading.Lock()
Next, every time shared_variable is to be modified, it is done after acquiring a lock. The lock is released when the variable has been modified, as shown in the following code:
self.lock.acquire()
self.shared_var += 1
self.lock.release()
This enables us to avoid a race condition. Since this code operates with a lock, it produces no change in the shared variable after an equal number of increments and decrements.
It seemed easy to use the lock mechanism to avoid a race condition. However, as the complexity of a program grows, there are many places where a variable may be modified. Tracking large code bases for places where a variable may be changed is often a difficult task.