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
