Introduction
Threading refers to the ability of a program to manage multiple threads of execution within a single process. A thread is a sequence of instructions within a program that can be executed independently of other threads. Threading allows multiple operations to run in parallel, making your program more efficient, especially on multi-core processors.
Why Use Threading?
- Concurrency: Threads run concurrently, enabling a program to perform multiple operations simultaneously.
- I/O-bound Operations: Ideal for tasks that involve waiting for external resources.
- Improved Performance: Helps in better CPU utilization for I/O-bound and network-bound applications.
Key Concepts
- Thread: A single sequence of execution within a process.
- Multithreading: The process of executing multiple threads concurrently.
- GIL: A mutex in Python that protects access to python objects, limiting the execution of threads to one at a time for CPU-bound operations.
When to Use Threading
- I/O-bound Tasks: For tasks like file reading/writing, web scraping, network communication
- Real-time Applications: In applications that require real-time updates like GUI applications or games.
- Background Tasks: For executing periodic or continuous background tasks like monitoring, logging, scheduled jobs.
Module
Creating a Thread: You can create a thread by instantiating the Thread class from the threading module.
import threading def fun(): for i in range(5): print(i) # creating a thread thread = threading.Thread(target=fun) # start the thread thread.start() # waiting for the thread to finish thread.join()
Using Thread Subclassing: Another way to create threads is by subclassing the Thread class and overriding the run method.
import threading class MyThread(threading.Thread): def run(self): for i in range(5): print(f"Thread {self.name}: {i}") thread = MyThread() thread.start() thread.join()
Synchronization with Locks: To avoid race condition when multiple threads access shared resources, use a Lock.
import threading counter = 0 lock = threading.Lock() def increment(): global counter # acquire the lock before modifying the counter with lock: counter += 1 threads = [] for _ in range(10): thread = threading.Thread(target=increment) threads.append(thread) thread.start() for thread in threads: thread.join() print(f"Final counter: {counter}")
Threading with ThreadPoolExecutor: The ThreadPoolExecutor from the concurrent.futures module simplifies thread management by using a pool of threads.
from concurrent.futures import ThreadPoolExecutor def square(n): return n*3 numbers = [1,2,3,4,5] with ThreadPoolExecutor() as executor: results = executor.map(square, numbers) print(list(results))
Daemon Threads: Daemon threads run in the background and automatically terminate when the main program exits.
import threading import time def background_task(): while True: print("Running in background.....") time.sleep(1) thread = threading.Thread(target=background_task) thread.daemon = True thread.start() time.sleep(5) print("Main thread finished.")
For More Blog Visit
threading — Thread-based parallelism — Python 3.13.1 documentation