Courses/Concurrency & Parallelism

Concurrency & Parallelism

Locks, RLock & Semaphore

Protect shared state from data races.

Even with the GIL, non-atomic operations (x += 1, read-modify-write on a dict) can interleave. Use a Lock to make a critical section atomic.

import threading

balance = 0
lock = threading.Lock()

def deposit(n):
    global balance
    with lock:                 # always prefer the context manager
        local = balance
        balance = local + n    # safe: no other thread sees a torn value

threads = [threading.Thread(target=deposit, args=(1,)) for _ in range(10_000)]
for t in threads: t.start()
for t in threads: t.join()
print(balance)  # 10000

**Which primitive when**

  • Lock — mutual exclusion. Can't be re-acquired by the same thread.
  • RLock — re-entrant; the same thread can acquire it multiple times (recursion, nested calls).
  • Semaphore(n) — at most n threads inside at once. Great for rate limiting.
  • Event — one-shot signal between threads (set() / wait()).
  • Condition — wait until a predicate becomes true (producer/consumer w/o queue).
  • Barrier(n) — block until n threads all arrive.

> 🧪 Concept-only — needs real OS threads.

Concept lesson

This topic relies on OS features the in-browser Python sandbox can't run (threads, subprocesses, sockets). Read the examples here, then try them in real CPython on your machine.

Sign in to track your progress across lessons.