Multithreading in Python: A Beginner-Friendly Guide


Multithreading is a programming technique that allows a program to run multiple tasks at the same time, within a single process. Think of it as multitasking for your code—like a chef cooking several dishes at once, instead of finishing one before starting the next
Why Use Multithreading in Python?
It makes programs faster and more responsive, especially when dealing with tasks that spend a lot of time waiting, such as downloading files, reading/writing to disk, or fetching data from the internet.
Multithreading is ideal for I/O-bound tasks—jobs that wait for input/output operations (like network or file access), not for heavy calculations
How Does Multithreading Work in Python?
Python provides a built-in threading
module to make working with threads easy.
Each thread runs a function independently, but all threads share the same memory space, making it easy to share data between them.
Threads are lighter than processes and start faster, but because they share memory, you need to be careful to avoid conflicts when multiple threads access the same data.
The Global Interpreter Lock (GIL): What You Need to Know
Python has a feature called the Global Interpreter Lock (GIL), which means only one thread can execute Python code at a time.
This sounds limiting, but for I/O-bound tasks, multithreading still provides big speedups because threads can run while others are waiting for I/O.
When Should You Use Multithreading?
Multithreading is best for:
Downloading many files or making lots of web requests at once (e.g., web scraping).
Reading/writing several files at the same time
Handling multiple users or network connections in a server
It’s not ideal for tasks that need a lot of CPU power (like crunching numbers or image processing). For those, use multiprocessing instead
How to Use Multithreading in Python
Here’s a super simple example:
import threading
import time
def print_numbers():
for i in range(5):
print(f"Number: {i}")
time.sleep(1)
# Create a thread that runs the print_numbers function
thread = threading.Thread(target=print_numbers)
thread.start()
# Main program continues running while thread works
print("Main program continues...")
thread.join() # Wait for thread to finish before exiting
What’s Happening Here?
We define a function to print numbers.
We create a thread to run that function.
The main program keeps going while the thread does its work.
join()
makes the main program wait for the thread to finish before exitingKey Concepts and Tips
Thread class: Core of the
threading
module; each thread is an instance of this class.start(): Begins the thread’s activity.
join(): Waits for the thread to finish.
Locks: Use these to prevent data corruption when multiple threads access shared data
Real-World Examples
Web Scraping: Fetching data from multiple websites at once3.
File Processing: Reading/writing multiple files in parallel.
Network Servers: Handling multiple users or connections at the same time.
Multithreading vs Multiprocessing
Feature | Multithreading | Multiprocessing |
Best for | I/O-bound tasks | CPU-bound tasks |
Memory usage | Low (threads share memory) | Higher (each process has its own) |
Python GIL | Affected | Not affected |
Communication | Easy (shared memory) | Harder (separate memory) |
Final Thoughts
Multithreading is a powerful tool for making Python programs faster and more efficient—when used for the right kind of tasks.
Remember: Use it for I/O-bound jobs, not heavy computations.
The
threading
module makes it easy to get started, even for beginners.
Deeper Understanding of Multithreading in Python
How Threads Share Memory
All threads in a process share:
Global variables
File descriptors
Standard input/output
This shared state is useful for communication but risky:
If two threads write to the same variable at once, race conditions can occur.
For example:
counter = 0 def increment(): global counter for _ in range(1000000): counter += 1 # Not thread-safe! t1 = threading.Thread(target=increment) t2 = threading.Thread(target=increment) t1.start() t2.start() t1.join() t2.join() print(counter) # May not be 2,000,000!
Avoiding Race Conditions: Locks
Use a
Lock
from thethreading
module:
lock = threading.Lock()
def thread_safe_increment():
global counter
for _ in range(1000000):
with lock: # Automatically acquires/releases the lock
counter += 1
More About the Global Interpreter Lock (GIL)
The GIL is a mutex that protects access to Python objects, preventing multiple native threads from executing Python bytecodes at once.
This limits the effectiveness of threads for CPU-bound tasks.
BUT: While one thread is waiting (e.g., on network or disk), the GIL is released, allowing another thread to run.
So, multithreading is still beneficial for:
Web scraping
Logging
File I/O
User input handling
But not for:
Image processing
Data crunching
ML model training
ThreadPoolExecutor (High-Level Alternative)
Python's concurrent.futures.ThreadPoolExecutor
makes multithreading even easier:
from concurrent.futures import ThreadPoolExecutor
def fetch_url(url):
# Simulate download
print(f"Fetching {url}")
time.sleep(2)
return f"{url} data"
urls = ["http://example.com", "http://test.com"]
with ThreadPoolExecutor(max_workers=2) as executor:
results = executor.map(fetch_url, urls)
for result in results:
print(result)
Benefits:
Cleaner syntax
Handles thread creation and management for you
Supports timeouts, exceptions, and return values
Best Practices
Use multithreading only if your bottlenecks are I/O-based.
Prefer
ThreadPoolExecutor
over raw threads for ease of use.Use
Queue
for thread-safe communication between threads.Avoid blocking calls in the main thread (especially in GUI apps).
Want More?
If you're ready to take it further:
Try
asyncio
for asynchronous I/O (great alternative to threading for I/O-bound tasks).Learn about
queue.Queue
for producer-consumer models.Explore Python’s
multiprocessing
module for parallel CPU-bound work.
Subscribe to my newsletter
Read articles from Deepesh Agrawal directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Deepesh Agrawal
Deepesh Agrawal
building shits