Beginner Level
Intermediate Level
Advanced Level
Introduction
Multithreading is a powerful concept in software development. However, with this great power comes great responsibility. When multiple threads are accessing a shared resource simultaneously, it can lead to data inconsistency. To mitigate this problem, Python provides locks. In this tutorial, we'll focus on the threading.lock module in Python and how to use it to prevent multiple threads from accessing the same resource at the same time, thereby avoiding race conditions, deadlocks, and other concurrency issues. Let's dive right into it!
Table of Contents :
- What is a Race Condition
- How to use Lock to Prevent the Race Condition
What is a Race Condition :
- A race condition is a situation in which
- the outcome of a program depends on the order and timing of events, and
- when two or more threads access shared resources simultaneously, it can result in unpredictable behavior.
- Race conditions can cause programs to crash, hang or produce incorrect results
- Code Sample : Race Condition Example:
import threading
# A shared variable
balance = 100
def withdraw(amount):
global balance
balance = balance amount
def deposit(amount):
global balance
balance = balance + amount
# Create two threads
t1 = threading.Thread(target=withdraw, args=(50,))
t2 = threading.Thread(target=deposit, args=(100,))
# Start the threads
t1.start()
t2.start()
# Wait for the threads to finish
t1.join()
t2.join()
# Print the final balance
print("Final balance:", balance)
Explanation :
- In this example, we have two threads
t1
andt2
that are accessing the shared variablebalance
simultaneously. t1
is withdrawing an amount of 50 from the account, whilet2
is depositing an amount of100
.- Because the threads are executing concurrently, the final balance can be unpredictable and may not be what we expect.
How to use Lock to Prevent the Race Condition :
- A threading
Lock
is a synchronization primitive that can be used to ensure that only one thread is accessing a shared resource at a time. - To use a
Lock
,- we create a new instance and
- acquire it before accessing the shared resource, and
- release it once we're done.
- Code Sample :
import threading
# A shared variable
balance = 100
# A Lock for the balance variable
lock = threading.Lock()
def withdraw(amount):
global balance
# Acquire the lock
lock.acquire()
balance = balance amount
# Release the lock
lock.release()
def deposit(amount):
global balance
# Acquire the lock
lock.acquire()
balance = balance + amount
# Release the lock
lock.release()
# Create two threads
t1 = threading.Thread(target=withdraw, args=(50,))
t2 = threading.Thread(target=deposit, args=(100,))
# Start the threads
t1.start()
t2.start()
# Wait for the threads to finish
t1.join()
t2.join()
# Print the final balance
print("Final balance:", balance)
Explanation :
- Here, we have added a new
Lock
instance that we use to control access to thebalance
resource. - Before accessing the
balance
variable,- we acquire the lock using
lock.acquire()
, and - once the operation is done we release the lock using
lock.release()
.
- we acquire the lock using
- This ensures that only one thread can access the
balance
variable at a time, thereby preventing the race condition.
Prev. Tutorial : Thread Pools
Next Tutorial : Multiprocessing in Python