Python Multithreading & Multiprocessing

Understanding parallel execution in Python

Introduction

In Python, multithreading and multiprocessing allow you to run multiple tasks in parallel, improving performance for certain types of programs.

While both aim to achieve parallel execution, they work differently under the hood due to Python's GIL.

Multithreading

Multithreading allows concurrent execution of multiple threads within the same process. It is best suited for I/O-bound tasks (like network requests or file operations).

Example:

import threading
import time

def print_numbers():
    for i in range(5):
        print(i)
        time.sleep(1)

# Create threads
t1 = threading.Thread(target=print_numbers)
t2 = threading.Thread(target=print_numbers)

# Start threads
t1.start()
t2.start()

# Wait for threads to finish
t1.join()
t2.join()
print("Done")
            

Multiprocessing

Multiprocessing runs multiple processes, each with its own Python interpreter and memory space. It is ideal for CPU-bound tasks (like heavy computations) because it bypasses the GIL.

Example:

from multiprocessing import Process
import time

def print_numbers():
    for i in range(5):
        print(i)
        time.sleep(1)

# Create processes
p1 = Process(target=print_numbers)
p2 = Process(target=print_numbers)

# Start processes
p1.start()
p2.start()

# Wait for processes to finish
p1.join()
p2.join()
print("Done")
            

Comparison: Multithreading vs Multiprocessing

Feature Multithreading Multiprocessing
Memory Shared memory within a process Separate memory for each process
Best For I/O-bound tasks CPU-bound tasks
Performance Limited by GIL in Python True parallel execution
Overhead Low, threads are lightweight Higher, processes consume more resources

References & Further Reading