Multithreading vs Multiprocessing in Python: Ultimate Concurrency Guide
Building concurrent applications in Python requires understanding how the Global Interpreter Lock (GIL) regulates background threads.
This tutorial details when to apply multithreading for I/O tasks and multiprocessing for heavy computational tasks.
1. Multithreading for I/O Bound Tasks
I/O bound operations spend most of their execution window waiting for network APIs or disk writes. The threading module allows concurrent execution during these wait cycles:
Python
import threading
import time
def fetch_api_data(endpoint):
print(f"Requesting endpoint: {endpoint}")
time.sleep(1.5) # Simulate network latency
print(f"Response parsed from {endpoint}")
endpoints = ["users", "settings", "orders"]
threads = []
for url in endpoints:
thread = threading.Thread(target=fetch_api_data, args=(url,))
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
print("All API network operations finished concurrently.")
2. Multiprocessing for CPU Bound Tasks
For intensive operations like math algorithms or image rendering, the GIL blocks parallel thread execution. The multiprocessing module spawns separate OS processes to bypass the GIL:
Python
from multiprocessing import Process
import os
def compute_square_root_series(start, end):
print(f"Processing on Process PID: {os.getpid()}")
result = sum(x ** 0.5 for x in range(start, end))
print(f"Calculated sum: {result}")
if __name__ == "__main__":
processes = []
# Divide the computational workload across multiple CPU cores
p1 = Process(target=compute_square_root_series, args=(1, 5000000))
p2 = Process(target=compute_square_root_series, args=(5000000, 10000000))
p1.start()
p2.start()
p1.join()
p2.join()
print("Parallel calculations completed successfully.")
Codecrown