Python - Multithreading
Modern applications often need to perform multiple tasks at the same time. For example, downloading files while playing music, or handling multiple users in a web server.
To achieve this, Python provides a concept called Multithreading.
Multithreading allows a program to run multiple threads (smaller units of a process) concurrently, making programs more efficient and responsive.
In this tutorial, you will learn what multithreading is, how it works in Python, how to create threads, and real-world examples.
What is Multithreading in Python?
Multithreading is a technique where multiple threads run within a single process.
A thread is the smallest unit of execution in a program.
In simple terms:
Multithreading = Running multiple tasks at the same time within one program.
Why Use Multithreading?
Multithreading is useful for:
- Improving performance of I/O tasks
- Handling multiple users in web applications
- Running background tasks
- Making programs more responsive
- Performing parallel-like execution
Importing Thread Module
Python provides a built-in module called threading.
import threadingCreating a Simple Thread
There are two common ways to create threads in Python:
- Using
threading.Thread - Extending the Thread class
Example 1: Using threading.Thread
import threading
def print_numbers():
for i in range(5):
print("Number:", i)
t1 = threading.Thread(target=print_numbers)
t1.start()
print("Main thread continues...")How It Works
- A new thread runs
print_numbers() - Main program continues execution
- Both run concurrently
Output Example
Number: 0
Number: 1
Main thread continues...
Number: 2
Number: 3
Number: 4(Execution order may vary)
Example 2: Multiple Threads
import threading
def task1():
for i in range(3):
print("Task 1:", i)
def task2():
for i in range(3):
print("Task 2:", i)
t1 = threading.Thread(target=task1)
t2 = threading.Thread(target=task2)
t1.start()
t2.start()
print("Main thread finished")What Happens Here?
- Two threads run simultaneously
- Task 1 and Task 2 execute independently
- Main thread continues execution
Using join() Method
The join() method waits for a thread to finish.
import threading
def task():
for i in range(3):
print("Working:", i)
t = threading.Thread(target=task)
t.start()
t.join()
print("Thread completed")Why join() is Important?
- Ensures thread completion
- Prevents premature program exit
- Helps synchronize execution
Thread Class Example (OOP Approach)
import threading
class MyThread(threading.Thread):
def run(self):
for i in range(3):
print("Thread running:", i)
t = MyThread()
t.start()What is GIL (Global Interpreter Lock)?
Python uses a mechanism called GIL.
Meaning:
Only one thread executes Python bytecode at a time.
This means:
- True CPU parallelism is limited
- Multithreading is best for I/O tasks, not heavy CPU tasks
When to Use Multithreading?
✔ File operations
✔ Network requests
✔ Web scraping
✔ API calls
✔ Background tasks
When NOT to Use Multithreading?
❌ Heavy CPU tasks (use multiprocessing instead)
❌ Complex shared memory operations
❌ Real-time scientific calculations
Example: Simulating Download Tasks
import threading
import time
def download(file):
print(f"Downloading {file}...")
time.sleep(2)
print(f"{file} downloaded")
t1 = threading.Thread(target=download, args=("file1.zip",))
t2 = threading.Thread(target=download, args=("file2.zip",))
t1.start()
t2.start()Thread Synchronization (Lock)
When multiple threads access shared data, problems may occur. Python provides a Lock.
import threading
lock = threading.Lock()
counter = 0
def increment():
global counter
for _ in range(100000):
lock.acquire()
counter += 1
lock.release()
t1 = threading.Thread(target=increment)
t2 = threading.Thread(target=increment)
t1.start()
t2.start()
t1.join()
t2.join()
print("Counter:", counter)Why Lock is Important?
Without lock:
- Data corruption may happen
- Race conditions occur
With lock:
- Only one thread modifies data at a time
Thread Pool Example (Advanced)
from concurrent.futures import ThreadPoolExecutor
def task(n):
return f"Task {n} completed"
with ThreadPoolExecutor(max_workers=3) as executor:
results = executor.map(task, range(5))
for r in results:
print(r)Advantages of Multithreading
- Faster execution for I/O tasks
- Better resource utilization
- Improved application responsiveness
- Efficient background processing
Disadvantages of Multithreading
- Complexity increases
- Debugging is harder
- Risk of race conditions
- Limited CPU performance due to GIL
Real-World Applications
Multithreading is used in:
- Web servers (Django, Flask)
- Chat applications
- Video streaming apps
- Web scraping tools
- File download managers
- Online games (light tasks)
Best Practices
1. Use join() Properly
Ensure threads complete execution.
2. Avoid Shared Data Issues
Use locks when needed.
3. Use Thread Pool for Large Tasks
Better performance and control.
4. Keep Threads Lightweight
Avoid heavy CPU computations.
Summary
Multithreading in Python allows multiple tasks to run concurrently, improving performance for I/O-bound applications. It is a powerful tool when used correctly, but must be managed carefully to avoid complexity and data issues.
Key Takeaways
- Multithreading runs multiple threads in one process
- Use
threadingmodule in Python - Best for I/O-bound tasks
- GIL limits CPU parallelism
- Use Lock to prevent data issues
- Thread pools improve scalability
Mastering multithreading helps you build faster, responsive, and efficient Python applications.


0 Comments