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:
- What is a Thread in Python?
- Starting a Thread Using the threading Module
- Using the Thread Class to Create Threads
- Passing Arguments to a Thread
- Using a Thread with a Class
- Thread Joins and Synchronization
- 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.