Skip to content

Thread Synchronization with Lock

The problem: race conditions

A race condition happens when two threads update shared state at the same time.

Example: incrementing a shared counter.

race_condition.py
import threading
 
counter = 0
 
 
def inc():
    global counter
    for _ in range(100_000):
        counter += 1
 
 
t1 = threading.Thread(target=inc)
t2 = threading.Thread(target=inc)
 
t1.start(); t2.start()
t1.join(); t2.join()
 
print("counter:", counter)
race_condition.py
import threading
 
counter = 0
 
 
def inc():
    global counter
    for _ in range(100_000):
        counter += 1
 
 
t1 = threading.Thread(target=inc)
t2 = threading.Thread(target=inc)
 
t1.start(); t2.start()
t1.join(); t2.join()
 
print("counter:", counter)

You might expect 200000, but results can vary.

Fix with Lock

lock_fix.py
import threading
 
counter = 0
lock = threading.Lock()
 
 
def inc():
    global counter
    for _ in range(100_000):
        with lock:
            counter += 1
 
 
t1 = threading.Thread(target=inc)
t2 = threading.Thread(target=inc)
 
t1.start(); t2.start()
t1.join(); t2.join()
 
print("counter:", counter)
lock_fix.py
import threading
 
counter = 0
lock = threading.Lock()
 
 
def inc():
    global counter
    for _ in range(100_000):
        with lock:
            counter += 1
 
 
t1 = threading.Thread(target=inc)
t2 = threading.Thread(target=inc)
 
t1.start(); t2.start()
t1.join(); t2.join()
 
print("counter:", counter)

Best practices

  • Keep locked sections small.
  • Prefer with lock:with lock: so you don’t forget to release.
  • Avoid deadlocks (don’t acquire multiple locks in random order).

πŸ§ͺ Try It Yourself

Exercise 1 – Basic Lock Usage

Exercise 2 – Lock as Context Manager

Exercise 3 – Detect Locked State

If this helped you, consider buying me a coffee β˜•

Buy me a coffee

Was this page helpful?

Let us know how we did