Introduction
Multithreading is a technique that allows a program to execute multiple tasks concurrently using threads within a single process.
A thread is the smallest unit of execution within a program. Multiple threads can run independently while sharing the same memory space.
Multithreading is commonly used for:
-
Web Scraping
-
File Processing
-
Network Operations
-
API Calls
-
Automation Frameworks
-
Background Tasks
-
Parallel Data Processing
In this tutorial, you will learn what threads are, how to create and manage them in Python, practical examples, automation testing use cases, common mistakes, and best practices.
What is a Thread?
A thread is a lightweight unit of execution inside a process.
Example
Imagine a browser:
-
One thread loads the webpage.
-
Another thread downloads images.
-
Another thread handles user interactions.
All these tasks run simultaneously.
Process vs Thread
| Process | Thread |
|---|---|
| Independent program | Part of a process |
| Own memory space | Shares memory |
| Heavyweight | Lightweight |
| Slower creation | Faster creation |
| More resources | Fewer resources |
Python Threading Module
Python provides the built-in threading module.
Import
import threading
Creating a Simple Thread
Example
import threading
def display_message():
print("Thread Running")
thread = threading.Thread(
target=display_message
)
thread.start()
Output
Thread Running
Understanding Thread Creation
thread = threading.Thread(
target=display_message
)
Parameters
| Parameter | Description |
|---|---|
| target | Function to execute |
| args | Arguments passed to function |
| name | Thread name |
| daemon | Background thread |
Starting a Thread
Use:
thread.start()
This creates a new thread and executes the target function.
Main Thread vs Child Thread
Example
import threading
def worker():
print("Worker Thread")
print("Main Thread Started")
thread = threading.Thread(
target=worker
)
thread.start()
print("Main Thread Finished")
Possible Output
Main Thread Started
Worker Thread
Main Thread Finished
Passing Arguments to Threads
Example
import threading
def greet(name):
print(f"Hello {name}")
thread = threading.Thread(
target=greet,
args=("John",)
)
thread.start()
Output
Hello John
Multiple Threads
Example
import threading
def task():
print("Task Executing")
for i in range(5):
thread = threading.Thread(
target=task
)
thread.start()
Output
Task Executing
Task Executing
Task Executing
Task Executing
Task Executing
Getting Current Thread Name
Example
import threading
def worker():
print(
threading.current_thread().name
)
thread = threading.Thread(
target=worker,
name="Worker-1"
)
thread.start()
Output
Worker-1
Using join()
join() waits for a thread to finish.
Example
import threading
import time
def task():
time.sleep(3)
print("Task Completed")
thread = threading.Thread(
target=task
)
thread.start()
thread.join()
print("Program Finished")
Output
Task Completed
Program Finished
Without join()
Example
thread.start()
print("Program Finished")
Possible Output
Program Finished
Task Completed
Creating Multiple Threads
Example
import threading
def worker(number):
print(
f"Thread {number} Running"
)
threads = []
for i in range(5):
thread = threading.Thread(
target=worker,
args=(i,)
)
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
Output
Thread 0 Running
Thread 1 Running
Thread 2 Running
Thread 3 Running
Thread 4 Running
Daemon Threads
Daemon threads run in the background.
Example
import threading
import time
def background_task():
while True:
print("Running")
time.sleep(1)
thread = threading.Thread(
target=background_task,
daemon=True
)
thread.start()
print("Main Program Ends")
When the main program exits, daemon threads terminate automatically.
Thread Identification
Example
import threading
def worker():
print(
threading.get_ident()
)
thread = threading.Thread(
target=worker
)
thread.start()
Output
140089321234432
(Example thread ID)
Real-World Example: Download Simulation
Sequential Execution
import time
for file in range(3):
print(
f"Downloading File {file}"
)
time.sleep(2)
Total time ≈ 6 seconds.
Multithreaded Execution
import threading
import time
def download(file):
print(
f"Downloading {file}"
)
time.sleep(2)
for file in range(3):
thread = threading.Thread(
target=download,
args=(file,)
)
thread.start()
Total time ≈ 2 seconds.
Threads in Selenium Automation
Use Cases
-
Parallel browser execution
-
Running multiple test cases
-
Data processing
-
Log collection
Example
import threading
def execute_test(test_name):
print(
f"Running {test_name}"
)
thread1 = threading.Thread(
target=execute_test,
args=("Login Test",)
)
thread2 = threading.Thread(
target=execute_test,
args=("Checkout Test",)
)
thread1.start()
thread2.start()
Threads in API Automation
Scenario
Run API calls simultaneously.
import threading
import requests
def call_api(url):
response = requests.get(url)
print(response.status_code)
thread1 = threading.Thread(
target=call_api,
args=("https://api.example.com/users",)
)
thread2 = threading.Thread(
target=call_api,
args=("https://api.example.com/orders",)
)
thread1.start()
thread2.start()
Common Mistakes Beginners Make
Forgetting start()
Incorrect
thread = threading.Thread(
target=worker
)
Thread never runs.
Correct
thread.start()
Calling Function Instead of Passing Reference
Incorrect
thread = threading.Thread(
target=worker()
)
Function executes immediately.
Correct
thread = threading.Thread(
target=worker
)
Forgetting join()
Incorrect
thread.start()
Main thread may finish early.
Correct
thread.start()
thread.join()
Sharing Data Unsafely
Multiple threads modifying the same variable can cause issues.
Use synchronization mechanisms such as:
threading.Lock()
Best Practices
Keep Threads Lightweight
Threads should perform specific tasks.
Use Meaningful Thread Names
name="LoginThread"
Use join() When Needed
Wait for important tasks to finish.
Handle Exceptions Inside Threads
try:
pass
except Exception as e:
print(e)
Use Thread Pools for Large Projects
Prefer:
from concurrent.futures import ThreadPoolExecutor
for better thread management.
Advantages of Multithreading
-
Improved responsiveness
-
Better resource utilization
-
Faster I/O operations
-
Suitable for network tasks
-
Supports concurrent execution
Limitations of Python Threads
-
Global Interpreter Lock (GIL)
-
Not ideal for CPU-intensive tasks
-
Requires synchronization for shared resources
Threads vs Multiprocessing
| Feature | Threads | Multiprocessing |
|---|---|---|
| Memory | Shared | Separate |
| Creation Cost | Low | High |
| Communication | Easy | Complex |
| CPU-bound Tasks | Limited | Better |
| I/O-bound Tasks | Excellent | Good |
Conclusion
Threads allow Python programs to perform multiple tasks concurrently within the same process. Using the threading module, developers can create, start, manage, and synchronize threads efficiently.
Multithreading is especially useful for I/O-bound tasks such as web requests, file handling, Selenium automation, API testing, and background processing.
Understanding threads is the foundation for building scalable and responsive Python applications.
Frequently Asked Questions (FAQs)
What is a thread?
A thread is the smallest unit of execution within a process.
Which module provides threading support?
import threading
How do I start a thread?
thread.start()
How do I wait for a thread to finish?
thread.join()
What is a daemon thread?
A background thread that automatically stops when the main program exits.
Key Takeaways
-
A thread is a lightweight unit of execution.
-
Python provides threading through the
threadingmodule. -
Use
Thread()to create threads. -
Use
start()to run a thread. -
Use
join()to wait for completion. -
Threads share the same memory space.
-
Ideal for I/O-bound tasks such as APIs and file handling.
-
Daemon threads run in the background.
-
Use
Lock()when sharing data between threads. -
Thread pools are recommended for large-scale applications.
