threading
multiprocessing
Python programming
concurrency
parallel computing

What are the differences between the threading and multiprocessing modules?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Python’s threading and multiprocessing modules both let you run work concurrently, but they solve different problems. Threads share one process and one memory space, while processes run as separate interpreters with separate memory, which changes performance, communication cost, and how the Global Interpreter Lock affects execution.

Threads Share Memory

The threading module creates multiple threads inside one Python process.

python
1import threading
2import time
3
4
5def worker(name):
6    print(f"start {name}")
7    time.sleep(1)
8    print(f"end {name}")
9
10threads = [threading.Thread(target=worker, args=(i,)) for i in range(3)]
11for t in threads:
12    t.start()
13for t in threads:
14    t.join()

All threads can access the same Python objects, which makes sharing data convenient. It also means you need locks or other synchronization tools when multiple threads modify shared state.

Processes Do Not Share Memory by Default

The multiprocessing module launches separate processes.

python
1from multiprocessing import Process
2import os
3import time
4
5
6def worker(name):
7    print(f"process {name} pid={os.getpid()}")
8    time.sleep(1)
9
10processes = [Process(target=worker, args=(i,)) for i in range(3)]
11for p in processes:
12    p.start()
13for p in processes:
14    p.join()

Each process has its own memory space, so one worker cannot simply mutate another worker’s variables. That isolation avoids many race conditions, but inter-process communication is more expensive than sharing data between threads.

The GIL Is the Big Reason the Choice Matters

In standard CPython, the Global Interpreter Lock allows only one thread at a time to execute Python bytecode. That means threads are often a poor way to speed up CPU-bound pure-Python work.

Processes avoid that limitation because each process has its own interpreter and its own GIL. That is why multiprocessing is often the better choice for heavy CPU work such as numerical loops, parsing, or image transformation written in Python.

Good Fit for threading

Threads are usually a good fit for I/O-bound tasks where workers spend much of their time waiting.

Examples include:

  • network requests
  • reading or writing files
  • talking to databases
  • waiting on sockets or APIs

While one thread waits for I/O, another can make progress.

Good Fit for multiprocessing

Processes are usually better when the work is CPU-bound and can be split across cores.

python
1from multiprocessing import Pool
2
3
4def square(x):
5    return x * x
6
7with Pool(4) as pool:
8    results = pool.map(square, [1, 2, 3, 4, 5])
9
10print(results)

This lets Python use multiple CPU cores more effectively than threads would for the same pure-Python computation.

Communication Cost Is Very Different

Threads can share a list, dictionary, or queue in memory directly. Processes usually need queues, pipes, managers, or serialization.

That matters because some workloads spend more time communicating than computing. In those cases, process overhead may outweigh the benefit of true parallel execution.

Startup and Memory Overhead

Threads are lighter to create. Processes are heavier because the operating system must start a new process with separate memory and runtime state. If you need thousands of very small short-lived workers, that overhead matters.

Common Pitfalls

A common mistake is using threads for CPU-bound code and expecting a big speedup under CPython. Another is switching to multiprocessing without realizing that arguments and results must usually be serialized between processes. Developers also often forget that shared mutable state is far easier in threads, which can be convenient but also introduces locking bugs. Finally, multiprocessing on some platforms requires the usual if __name__ == "__main__": guard to avoid recursive process spawning.

Summary

  • 'threading runs multiple threads inside one process and shares memory.'
  • 'multiprocessing runs separate processes with separate memory.'
  • Threads are usually better for I/O-bound work.
  • Processes are usually better for CPU-bound work in CPython because they avoid the single-process GIL bottleneck.
  • Choose based on workload shape, communication cost, and memory model rather than treating the two modules as interchangeable.

Course illustration
Course illustration

All Rights Reserved.