advanced

Multithreading in Python

7 min read

Overview

Multithreading allows a Python application to run multiple tasks concurrently within a single process. It is ideal for I/O-bound tasks (like downloading web pages, reading disk files, or querying network APIs).

Learning Objectives

  • Use the threading module to launch threads.
  • Use concurrent.futures.ThreadPoolExecutor for worker pool management.
  • Understand how the Global Interpreter Lock (GIL) impacts thread concurrency.

Concept Explanation

In simple words:

  • I/O-Bound Operations: When a thread waits for network data or disk I/O, Python releases the GIL so other threads can execute. Multithreading makes network downloads 5x–10x faster!
  • CPU-Bound Operations: For heavy math calculation, the GIL prevents multiple threads from running CPU instructions simultaneously on multiple CPU cores. For CPU tasks, use Multiprocessing instead.

Code Examples

Example 1 — Concurrent Downloads with ThreadPoolExecutor

python
from concurrent.futures import ThreadPoolExecutor
import time

urls = [
    "https://api.example.com/data1",
    "https://api.example.com/data2",
    "https://api.example.com/data3",
]

def fetch_url(url):
    print(f"Starting fetch: {url}")
    time.sleep(1) # Simulate network response delay
    return f"Data from {url}"

# Run downloads concurrently across 3 worker threads
start_time = time.time()
with ThreadPoolExecutor(max_workers=3) as executor:
    results = list(executor.map(fetch_url, urls))

elapsed = time.time() - start_time
print(f"Fetched {len(results)} URLs concurrently in {elapsed:.2f} seconds!")

Example 2 — Thread Safety with Lock

python
import threading

counter = 0
lock = threading.Lock()

def increment():
    global counter
    for _ in range(100000):
        # Acquire lock to prevent race conditions
        with lock:
            counter += 1

threads = [threading.Thread(target=increment) for _ in range(4)]
for t in threads: t.start()
for t in threads: t.join()

print("Final Counter (Race Condition Free):", counter)

Visual Flow

The following execution flow represents multithreaded I/O execution:

⚡ Visual Execution Flow
Launch Worker Thread PoolThread 1 Waits on I/O (GIL Released)Thread 2 Executes ConcurrentlyGather Future Results

Common Mistakes

  • Using Threads for Heavy Math: Expecting multithreading to speed up CPU-bound tasks like matrix multiplication. Use multiprocessing for CPU tasks.
  • Race Conditions: Modifying shared global data across threads without thread locks (threading.Lock).

Best Practices

  • Always use concurrent.futures.ThreadPoolExecutor context managers for thread management.