threading
The threading module implements higher-level threading interfaces on top of the lower-level _thread module. It provides a Thread class for creating and managing threads, synchronization primitives like Lock, RLock, Condition, Semaphore, Event, and Barrier, and utility functions for working with threads.
Syntax
import threading
Core Classes
Thread
The Thread class represents a thread of execution. The most common way to use it is to subclass Thread and override the run() method, or to pass a callable target to the constructor.
import threading
def worker():
print("Worker thread running")
thread = threading.Thread(target=worker)
thread.start()
thread.join()
Lock
A Lock is a synchronization primitive that can be acquired by only one thread at a time. It ensures exclusive access to a shared resource.
lock = threading.Lock()
def safe_increment():
with lock:
# critical section
global counter
counter += 1
RLock
A reentrant lock (RLock) can be acquired multiple times by the same thread without deadlocking. This is useful for recursive functions that need to lock the same resource.
rlock = threading.RLock()
def recursive_func(n):
with rlock:
if n > 0:
recursive_func(n - 1)
Condition
A Condition variable allows one or more threads to wait until they are notified by another thread. It is associated with a lock.
condition = threading.Condition()
queue = []
def consumer():
with condition:
while not queue:
condition.wait()
item = queue.pop()
print(f"Consumed {item}")
def producer(item):
with condition:
queue.append(item)
condition.notify()
Semaphore
A Semaphore manages a counter that limits the number of threads that can access a resource concurrently.
semaphore = threading.Semaphore(3) # allow up to 3 threads
def limited_access():
with semaphore:
# do work
pass
Event
An Event is a simple synchronization object that allows one thread to signal an event that other threads are waiting for.
event = threading.Event()
def waiter():
print("Waiting for event")
event.wait()
print("Event set")
def setter():
time.sleep(2)
event.set()
Barrier
A Barrier ensures a fixed number of threads wait for each other before proceeding past a certain point.
barrier = threading.Barrier(3)
def party():
print("Waiting at barrier")
barrier.wait()
print("Barrier crossed")
Common Patterns
Daemon Threads
Daemon threads are terminated when the main program exits, regardless of whether they are finished. Set daemon=True before starting the thread.
thread = threading.Thread(target=worker, daemon=True)
thread.start()
# main program can exit without waiting for thread
Thread‑Local Data
threading.local() creates thread‑local storage where each thread sees its own copy of the data.
local_data = threading.local()
def set_name(name):
local_data.name = name
def get_name():
return getattr(local_data, "name", "unknown")
Thread Pools Using ThreadPoolExecutor
Although threading does not provide a built‑in thread pool, the concurrent.futures.ThreadPoolExecutor offers a high‑level interface for managing a pool of worker threads.
from concurrent.futures import ThreadPoolExecutor
def task(n):
return n * n
with ThreadPoolExecutor(max_workers=4) as executor:
results = executor.map(task, range(10))
Errors
Common errors when using the threading module:
RuntimeError: cannot join current thread – attempting to join the thread from within itself.RuntimeError: threads can only be started once – callingstart()on an already‑started thread.- Deadlocks – when two or more threads wait for each other to release locks.
- Race conditions – unsynchronized access to shared mutable state leading to unpredictable behavior.