When using global variables in Python multithreading, you may experience unexpected behavior due to race conditions, shared state issues, and improper synchronization.
1. Common Issues and Fixes
A. Race Condition: Multiple Threads Modifying a Global Variable
A race condition occurs when multiple threads access and modify a shared global variable simultaneously, leading to unpredictable results.
Example (Unexpected Behavior)
import threading
counter = 0 # Global variable
def increment():
global counter
for _ in range(1000000):
counter += 1 # Not atomic, can cause incorrect values
threads = []
for _ in range(5):
t = threading.Thread(target=increment)
t.start()
threads.append(t)
for t in threads:
t.join()
print("Final counter value:", counter) # Expected: 5,000,000 | Actual: Unpredictable!
Issue: counter += 1
is not atomic; it involves read → modify → write, which can be interrupted by other threads, causing lost updates.
Fix: Use threading.Lock()
lock = threading.Lock()
counter = 0
def increment():
global counter
for _ in range(1000000):
with lock:
counter += 1 # Now safe
threads = [threading.Thread(target=increment) for _ in range(5)]
for t in threads:
t.start()
for t in threads:
t.join()
print("Final counter value:", counter) # Correct: 5,000,000
B. Global Variable Not Updating Across Threads
Sometimes, threads seem unable to update a global variable correctly.
Example (Unexpected Behavior)
import threading
flag = False # Global variable
def worker():
global flag
flag = True # Change not always visible in other threads
t = threading.Thread(target=worker)
t.start()
t.join()
print("Flag:", flag) # Expected: True | Actual: Sometimes False!
Issue: Due to caching optimizations, a thread may read a stale version of the global variable.
Fix: Use threading.Event()
to safely share state
event = threading.Event()
def worker():
event.set() # This safely updates the flag
t = threading.Thread(target=worker)
t.start()
t.join()
print("Flag:", event.is_set()) # Always True
C. Inconsistent Data Due to Shared List or Dictionary
Modifying shared lists or dictionaries without locks can cause data corruption.
Example (Unexpected Behavior)
import threading
data = []
def add_item():
for _ in range(1000):
data.append(1) # Multiple threads modifying the list
threads = [threading.Thread(target=add_item) for _ in range(5)]
for t in threads:
t.start()
for t in threads:
t.join()
print("List length:", len(data)) # Expected: 5000 | Actual: Unpredictable!
Issue: Threads may overwrite or skip writes, leading to inconsistent data.
Fix: Use threading.Lock()
lock = threading.Lock()
data = []
def add_item():
for _ in range(1000):
with lock:
data.append(1) # Now thread-safe
threads = [threading.Thread(target=add_item) for _ in range(5)]
for t in threads:
t.start()
for t in threads:
t.join()
print("List length:", len(data)) # Correct: 5000
2. Summary of Fixes
Issue | Fix |
---|---|
Race conditions (global variable modified by multiple threads) | Use threading.Lock() |
Global variable not updating correctly | Use threading.Event() |
Shared lists/dictionaries getting corrupted | Use threading.Lock() |