Introduction
Modern applications often need to perform multiple tasks simultaneously. For example, a web browser can download files while displaying web pages, and a chat application can send and receive messages at the same time.
Python provides Multithreading, a technique that allows multiple threads to run within a single process. Threads share the same memory space and can execute tasks concurrently, making them especially useful for I/O-bound operations such as file handling, network communication, and database access.
Python’s built-in threading module makes it easy to create and manage threads. By using multithreading, developers can build faster and more responsive applications.
What is Multithreading?
Multithreading is the process of executing multiple threads within a single process.
A thread is the smallest unit of execution in a program.
Instead of executing tasks one after another:
Task 1
↓
Task 2
↓
Task 3
Multithreading allows Task 1, Task 2, and Task 3 to run concurrently.
This improves application responsiveness and resource utilization.
Why Use Multithreading?
Multithreading offers several advantages:
1. Faster Execution
Multiple tasks can progress simultaneously.
2. Improved User Experience
Applications remain responsive while background tasks execute.
3. Better Resource Utilization
Threads share memory efficiently.
4. Simplified Concurrent Programming
Allows handling multiple operations within a single application.
5. Ideal for I/O Operations
Useful for:
- File handling
- Database access
- API calls
- Web scraping
- Downloads
Process vs Thread
| Feature | Process | Thread |
|---|---|---|
| Memory | Separate | Shared |
| Creation Cost | High | Low |
| Speed | Slower | Faster |
| Communication | Complex | Easy |
| Resource Usage | Higher | Lower |
Python Threading Module
Python provides the built-in threading module.
Import it:
import threading
This module contains classes and methods for creating and managing threads.
Creating a Thread
Syntax
thread = threading.Thread(
target=function_name
)
Start the thread:
thread.start()
Example: Simple Thread
import threading
def task():
print("Thread Running")
thread = threading.Thread(
target=task
)
thread.start()
Output:
Example: Multiple Threads
import threading
def task():
print("Task Executed")
thread1 = threading.Thread(
target=task
)
thread2 = threading.Thread(
target=task
)
thread1.start()
thread2.start()
Output:
Task Executed
Multiple threads run concurrently.
Passing Arguments to Threads
You can pass parameters using the args argument.
Example:
import threading
def greet(name):
print(
f"Hello {name}"
)
thread = threading.Thread(
target=greet,
args=("John",)
)
thread.start()
Output:
Using join()
The join() method waits for a thread to finish execution.
Example:
import threading
import time
def task():
time.sleep("2")
print("Task Completed")
thread = threading.Thread(
target=task
)
thread.start()
thread.join()
print("Program Finished")
Output:
Program Finished
Current Thread Information
Example:
import threading
print(
threading.current_thread()
)
Output:
Naming Threads
import threading
def task():
print(
threading.current_thread().name
)
thread = threading.Thread(
target=task,
name="WorkerThread"
)
thread.start()
Output:
Creating Threads Using Thread Class
You can extend the Thread class.
Example:
import threading
class MyThread(
threading.Thread
):
def run(self):
print(
"Custom Thread Running"
)
thread = MyThread()
thread.start()
Output:
Daemon Threads
Daemon threads run in the background.
Example:
import threading
def background_task():
while True:
pass
thread = threading.Thread(
target=background_task,
daemon=True
)
thread.start()
Daemon threads terminate automatically when the main program exits.
Multithreading Example: Countdown Timer
import threading
import time
def countdown():
for i in range(5, 0, -1):
print(i)
time.sleep("1")
thread = threading.Thread(
target=countdown
)
thread.start()
Output:
4
3
2
1
Multithreading Example: File Download Simulation
import threading
import time
def download(file):
print(
f"Downloading {file}"
)
time.sleep("2")
print(
f"{file} Downloaded"
)
files = [
"file1.zip",
"file2.zip",
"file3.zip"
]
#for file in files:
thread = threading.Thread(
target=download,
args=(file,)
)
thread.start()
Output:
Downloading file2.zip
Downloading file3.zip
Downloads occur concurrently.
Multithreading Example: Web Scraping
import threading
def scrape(url):
print(
f"Scraping {url}"
)
urls = [
"site1.com",
"site2.com",
"site3.com"
]
#for url in urls:
thread = threading.Thread(
target=scrape,
args=(url,)
)
thread.start()
This speeds up data collection.
Multithreading Example: Sending Emails
import threading
def send_email(user):
print(
f"Email Sent To {user}"
)
users = [
"John",
"Mike",
"Sara"
]
#for user in users:
threading.Thread(
target=send_email,
args=(user,)
).start()
Output:
Email Sent To Mike
Email Sent To Sara
Thread Synchronization
When multiple threads share resources, synchronization is required.
Example:
counter = 0
Multiple threads updating the same variable may cause race conditions.
Race Condition Example
import threading
counter = 0
def increment():
global counter
for i in range(1000):
counter += 1
Results may become inconsistent.
Using Lock for Synchronization
import threading
lock = threading.Lock()
counter = 0
def increment():
global counter
for i in range(1000):
with lock:
counter += 1
Locks prevent race conditions.
Python GIL and Multithreading
Python uses a mechanism called the Global Interpreter Lock (GIL).
The GIL allows only one thread to execute Python bytecode at a time.
Because of GIL:
- CPU-bound tasks gain little benefit from threads.
- I/O-bound tasks benefit significantly.
Examples of I/O-bound tasks:
- File operations
- Database queries
- API requests
- Network communication
Common Thread Methods
| Method | Purpose |
|---|---|
| start() | Starts a thread |
| join() | Waits for completion |
| is_alive() | Checks if thread is running |
| current_thread() | Returns current thread |
| Lock() | Creates a lock |
| RLock() | Creates a reentrant lock |
Advantages of Multithreading
| Advantage | Description |
|---|---|
| Faster I/O Operations | Improves responsiveness |
| Shared Memory | Easy communication |
| Lower Resource Usage | Efficient execution |
| Better User Experience | Prevents application freezing |
| Concurrent Execution | Handles multiple tasks |
Common Mistakes
1. Forgetting start()
Incorrect:
thread = threading.Thread(
target=task
)
Correct:
thread.start()
2. Ignoring join()
Threads may not complete before the program exits.
Use:
thread.join()
3. Not Using Locks
Shared resources can cause race conditions.
4. Creating Too Many Threads
Excessive threads can reduce performance.
5. Using Threads for CPU-Bound Tasks
For heavy computation, use multiprocessing instead.
Conclusion
Python Multithreading is a powerful technique that enables multiple tasks to run concurrently within a single process.
Multithreading is especially beneficial for I/O-bound tasks such as web scraping, file processing, downloads, and network communication. Although Python’s Global Interpreter Lock (GIL) limits performance improvements for CPU-intensive tasks, multithreading remains an essential tool for modern Python development.