Skip to main content Brad's PyNotes

Threading Module

TL;DR

The threading module enables concurrent execution through threads with synchronization primitives like Lock, Event, and Semaphore for safe resource sharing.

Interesting!

Python’s Global Interpreter Lock (GIL) means only one thread executes Python code at once - threading helps with I/O-bound tasks, not CPU-bound ones!

Creating Threads

python code snippet start

import threading
import time

def worker(name, delay):
    print(f"Worker {name} starting")
    time.sleep(delay)
    print(f"Worker {name} finished")

# Create and start threads
threads = []
for i in range(3):
    t = threading.Thread(target=worker, args=(f"Thread-{i}", 2))
    threads.append(t)
    t.start()

# Wait for all to complete
for t in threads:
    t.join()

python code snippet end

Thread Synchronization

python code snippet start

import threading

# Shared resource protection
lock = threading.Lock()
counter = 0

def increment():
    global counter
    with lock:  # Automatically acquire/release
        counter += 1

# Event signaling
event = threading.Event()

def waiter():
    event.wait()  # Block until event is set
    print("Event received!")

def setter():
    time.sleep(2)
    event.set()  # Signal waiting threads

python code snippet end

Semaphore for Resource Limiting

python code snippet start

# Limit concurrent access to resource
semaphore = threading.Semaphore(2)  # Max 2 threads

def limited_resource(thread_id):
    with semaphore:
        print(f"Thread {thread_id} accessing resource")
        time.sleep(3)
        print(f"Thread {thread_id} releasing resource")

python code snippet end

Thread-Safe Communication

python code snippet start

import queue
import threading

# Thread-safe queue
q = queue.Queue()

def producer():
    for i in range(5):
        q.put(f"item-{i}")
        print(f"Produced item-{i}")

def consumer():
    while True:
        item = q.get()
        if item is None:
            break
        print(f"Consumed {item}")
        q.task_done()

python code snippet end

Best Practices

  • Use with statements for locks to ensure release
  • Call join() to wait for thread completion
  • Use queue.Queue for thread-safe data exchange
  • Consider concurrent.futures for simpler thread pools

Threading excels at I/O-bound tasks where threads spend time waiting - perfect for web scraping, file operations, and network requests! Compare threading with asyncio for async I/O and multiprocessing for CPU-bound tasks . For thread-safe data handling, use SQLite databases and thread-safe logging .

Reference: threading — Thread-based parallelism