Ad – 728Γ—90
πŸš€ Advanced Python

Python Multithreading – Concurrent Execution with Threads

Multithreading lets your program do multiple things at the same time. Python threads are ideal for I/O-bound tasks: network requests, file operations, database queries. Understanding the GIL (Global Interpreter Lock) helps you choose between threads and processes for different workloads.

⏱️ 25 min read🎯 AdvancedπŸ“… Updated 2026

Creating Threads

Use threading.Thread to create and start threads.

Python
import threading
import time

def download(site):
    print(f"Downloading {site}...")
    time.sleep(2)   # Simulate network I/O
    print(f"Done: {site}")

# Without threads: takes 6 seconds
# With threads: takes ~2 seconds
threads = []
for site in ["google.com", "github.com", "python.org"]:
    t = threading.Thread(target=download, args=(site,))
    threads.append(t)
    t.start()

for t in threads:
    t.join()   # Wait for all to complete
print("All downloads done")
β–Ά Output
Downloading google.com... Downloading github.com... Downloading python.org... Done: google.com Done: github.com Done: python.org All downloads done

The GIL – Global Interpreter Lock

The GIL prevents multiple threads from executing Python bytecode simultaneously. This means threads DO NOT speed up CPU-bound tasks (computation). They DO help I/O-bound tasks because threads release the GIL while waiting for I/O.

⚠️
Note

Use threading for I/O-bound tasks (HTTP, files, databases). Use multiprocessing for CPU-bound tasks (calculations, image processing). Using threads for heavy computation will NOT improve performance due to the GIL.

Thread Safety – Using Locks

When threads share data, use a Lock to prevent race conditions.

Python
import threading

counter = 0
lock = threading.Lock()

def increment():
    global counter
    for _ in range(100_000):
        with lock:      # Only one thread at a time
            counter += 1

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

print(f"Counter: {counter}")  # Always 500000 with lock
β–Ά Output
Counter: 500000
Ad – 336Γ—280

ThreadPoolExecutor – High-Level API

concurrent.futures.ThreadPoolExecutor is the modern, higher-level threading API.

Python
from concurrent.futures import ThreadPoolExecutor
import time

def fetch(url):
    time.sleep(1)   # Simulate I/O
    return f"Data from {url}"

urls = ["api.com/1", "api.com/2", "api.com/3"]

with ThreadPoolExecutor(max_workers=3) as executor:
    results = list(executor.map(fetch, urls))

for r in results:
    print(r)
β–Ά Output
Data from api.com/1 Data from api.com/2 Data from api.com/3