Python - Multiprocessing vs Multithreading in Python (Advanced Concurrency)

Concurrency is about running multiple tasks in overlapping time periods. In Python, two major ways to achieve this are multithreading and multiprocessing. Although they sound similar, they behave very differently due to how Python is implemented.


1. Multithreading

What it is

Multithreading allows a program to run multiple threads within a single process. Threads share the same memory space and resources.

Key characteristics

  • Threads are lightweight compared to processes

  • All threads share the same variables and memory

  • Communication between threads is easy (shared memory)

  • Context switching is faster than processes

Limitation: GIL (Global Interpreter Lock)

Python has something called the Global Interpreter Lock (GIL), which ensures that only one thread executes Python bytecode at a time. This means:

  • Even if you create multiple threads, only one thread runs at a time for CPU-bound tasks

  • Threads are still useful for I/O-bound tasks (like file reading, network calls)

When to use multithreading

Use it when your program spends time waiting, such as:

  • Downloading files from the internet

  • Reading/writing files

  • Calling APIs

  • Database operations

Example

import threading

def task():
    for i in range(5):
        print("Thread running")

t1 = threading.Thread(target=task)
t2 = threading.Thread(target=task)

t1.start()
t2.start()

t1.join()
t2.join()

2. Multiprocessing

What it is

Multiprocessing creates multiple processes, each with its own memory space and Python interpreter.

Key characteristics

  • Each process runs independently

  • No shared memory by default

  • True parallel execution (can use multiple CPU cores)

  • Higher memory usage than threads

Advantage over multithreading

Multiprocessing bypasses the GIL because each process has its own Python interpreter. This allows:

  • Real parallel execution

  • Better performance for CPU-heavy tasks

When to use multiprocessing

Use it for CPU-intensive tasks such as:

  • Mathematical computations

  • Data processing

  • Image processing

  • Machine learning workloads

Example

from multiprocessing import Process

def task():
    for i in range(5):
        print("Process running")

p1 = Process(target=task)
p2 = Process(target=task)

p1.start()
p2.start()

p1.join()
p2.join()

3. Key Differences

Feature Multithreading Multiprocessing
Execution Not truly parallel (due to GIL) Truly parallel
Memory Shared memory Separate memory
Speed (CPU tasks) Slower Faster
Speed (I/O tasks) Faster Slight overhead
Communication Easy Complex (pipes, queues)
Resource usage Low High

4. Practical Understanding

Think of it like this:

  • Multithreading is like multiple workers sharing one kitchen. They can easily share ingredients, but only one can use the stove at a time.

  • Multiprocessing is like multiple separate kitchens. Each worker has their own setup, so they can cook simultaneously without interference.


5. When to Choose What

Choose multithreading if:

  • Your program waits a lot (I/O-bound)

  • You need quick communication between tasks

  • Memory usage should be low

Choose multiprocessing if:

  • Your program is CPU-heavy

  • You want to use multiple cores

  • Performance is critical


6. Common Mistakes

  • Using threads for CPU-heavy work and expecting speedup

  • Ignoring overhead of creating processes

  • Not handling shared data correctly in multiprocessing

  • Forgetting to use if __name__ == "__main__": in multiprocessing (important on Windows)


7. Summary

Multithreading is best for handling multiple waiting tasks efficiently, while multiprocessing is designed for executing heavy computations in parallel. The choice depends mainly on whether your problem is I/O-bound or CPU-bound.