Home » Python Threading: Starting a Thread Tutorial with Examples

Python Threading: Starting a Thread Tutorial with Examples

Threading is a feature in Python that allows concurrent execution of two or more parts of a program.

Threads are lighter than processes, and multiple threads can be used to perform tasks in parallel, improving performance for I/O-bound and network-related tasks.

In this tutorial, we will focus on how to start a thread in Python using the threading module.

In this tutorial, we will cover:

  1. What is a Thread in Python?
  2. Starting a Thread Using the threading Module
  3. Using the Thread Class to Create Threads
  4. Passing Arguments to a Thread
  5. Using a Thread with a Class
  6. Thread Joins and Synchronization
  7. Examples and Use Cases

Let’s dive into each concept with examples!

1. What is a Thread in Python?

A thread is a lightweight process that runs concurrently with other threads. Python provides the threading module to create and manage threads. Each thread runs in parallel with the main program, enabling tasks such as waiting for network responses, reading files, or running multiple functions at once.

In Python, threads run concurrently but not in true parallel due to the Global Interpreter Lock (GIL). For CPU-bound tasks, you might prefer using multiprocessing, but for I/O-bound tasks, threading can improve performance.

2. Starting a Thread Using the threading Module

The threading module allows you to create and start threads. The simplest way to start a thread is by using the Thread() class from the module.

Example 1: Starting a Simple Thread

import threading
import time

def print_numbers():
    for i in range(1, 6):
        time.sleep(1)
        print(f"Number: {i}")

# Create a thread and start it
thread = threading.Thread(target=print_numbers)
thread.start()

# The main thread continues executing
print("Main thread is running.")

Explanation:

  • A thread is created using threading.Thread(), with the function print_numbers() passed as the target.
  • The start() method is called to begin the thread, and the main thread continues executing without waiting for the child thread to finish.

3. Using the Thread Class to Create Threads

You can create a custom thread by subclassing the Thread class and overriding its run() method. This allows you to define a thread’s behavior directly within a class.

Example 2: Creating a Custom Thread Class

import threading
import time

class MyThread(threading.Thread):
    def run(self):
        for i in range(5):
            time.sleep(1)
            print(f"Thread {self.name} is printing: {i}")

# Create two threads
thread1 = MyThread()
thread2 = MyThread()

# Start the threads
thread1.start()
thread2.start()

Explanation:

  • A custom class MyThread is created by subclassing threading.Thread.
  • The run() method defines the thread’s task.
  • Two threads (thread1 and thread2) are created and started, each running concurrently.

4. Passing Arguments to a Thread

You can pass arguments to a thread by providing the args parameter in the Thread() constructor.

Example 3: Passing Arguments to a Thread

import threading
import time

def print_message(message, repeat):
    for i in range(repeat):
        time.sleep(1)
        print(message)

# Create and start a thread, passing arguments
thread = threading.Thread(target=print_message, args=("Hello, World!", 3))
thread.start()

Explanation:

  • The print_message() function takes two arguments: a message and a repeat count.
  • When creating the thread, the args parameter is used to pass the arguments to the function.

5. Using a Thread with a Class

You can create threads in Python that are associated with specific class methods, allowing threads to run tasks as part of object instances.

Example 4: Threading with Class Methods

import threading
import time

class Task:
    def print_task(self, message):
        for i in range(3):
            time.sleep(1)
            print(f"{message}: {i}")

# Create an instance of the class
task = Task()

# Start a thread using a method from the class
thread = threading.Thread(target=task.print_task, args=("Threading in class",))
thread.start()

Explanation:

  • A thread is started using the print_task() method of the Task class.
  • The class method is executed concurrently with the main program.

6. Thread Joins and Synchronization

The join() method ensures that the main thread waits for the child thread to complete before proceeding. This is useful when you want to ensure that a thread has finished before continuing with other parts of the program.

Example 5: Using join() to Wait for Threads

import threading
import time

def task():
    for i in range(5):
        time.sleep(1)
        print(f"Task running: {i}")

# Create and start a thread
thread = threading.Thread(target=task)
thread.start()

# Wait for the thread to finish
thread.join()
print("Main thread continues after the thread has finished.")

Explanation:

  • The join() method blocks the main thread until the child thread (task()) finishes its execution.
  • The message “Main thread continues…” is printed after the thread has completed.

Example 6: Synchronizing Threads with a Lock

In cases where multiple threads need to access shared resources, you can use a Lock to prevent race conditions.

import threading
import time

lock = threading.Lock()

class BankAccount:
    def __init__(self, balance):
        self.balance = balance

    def withdraw(self, amount):
        lock.acquire()
        time.sleep(1)  # Simulate processing delay
        self.balance -= amount
        print(f"Withdrawn {amount}, New Balance: {self.balance}")
        lock.release()

account = BankAccount(100)

# Create two threads trying to withdraw from the same account
thread1 = threading.Thread(target=account.withdraw, args=(50,))
thread2 = threading.Thread(target=account.withdraw, args=(30,))

# Start both threads
thread1.start()
thread2.start()

# Wait for both threads to finish
thread1.join()
thread2.join()

Explanation:

  • A Lock is used to prevent both threads from modifying the balance of the BankAccount simultaneously.
  • The acquire() method locks the critical section of the code, and release() unlocks it after the operation is done.

7. Examples and Use Cases

Example 7: Running Multiple Threads

You can create multiple threads to perform several tasks concurrently.

import threading
import time

def task(name, seconds):
    for i in range(seconds):
        time.sleep(1)
        print(f"{name} is running: {i}")

# Create and start multiple threads
threads = []
for i in range(3):
    thread = threading.Thread(target=task, args=(f"Thread-{i+1}", 5))
    threads.append(thread)
    thread.start()

# Wait for all threads to finish
for thread in threads:
    thread.join()

print("All threads have finished.")

Explanation:

  • Three threads are created and started, each running the task() function.
  • The join() method ensures that the main program waits for all threads to finish before continuing.

Example 8: Daemon Threads

Daemon threads are background threads that automatically terminate when the main program exits.

import threading
import time

def background_task():
    while True:
        print("Daemon thread running...")
        time.sleep(2)

# Create a daemon thread
daemon_thread = threading.Thread(target=background_task)
daemon_thread.daemon = True
daemon_thread.start()

# Main thread sleeps for 5 seconds before exiting
time.sleep(5)
print("Main thread is exiting, daemon thread will stop.")

Explanation:

  • The daemon = True flag makes the thread a daemon thread. It will run in the background but will be terminated when the main program exits, even if the task is incomplete.

Summary of Key Concepts for Threading

Concept Description
threading.Thread Creates a new thread using a function as the target.
start() Starts the execution of the thread.
join() Blocks the main thread until the child thread has finished.
args Passes arguments to the target function when creating a thread.
Subclassing Create custom threads by subclassing the Thread class and overriding the run() method.
Lock() Synchronizes threads and prevents race conditions when accessing shared resources.
daemon Sets a thread as a daemon, meaning it will automatically terminate when the main program exits.

Conclusion

Threading in Python allows for concurrent execution of tasks, improving efficiency in I/O-bound applications such as file handling or network requests. In this tutorial, we covered:

  • How to start a thread using the threading module.
  • Creating custom threads by subclassing the Thread class.
  • Passing arguments to threads and using threads in class methods.
  • Using join() to synchronize threads and ensure proper completion.
  • Managing thread safety with Lock() and creating daemon threads.

You may also like

Leave a Comment

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More