Threads

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 threading module.

  • 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.