Multithreading in Python allows you to run multiple threads (smaller units of a process) concurrently, making your programs more efficient and responsive. It is particularly useful in I/O-bound operations such as file handling, network requests, and user interface updates.
1. What is Multithreading?
Multithreading is a concurrent execution technique in which multiple threads share the same process memory space. Each thread operates independently while sharing resources with other threads in the same process.
2. The threading Module in Python
Python provides the threading module for creating and managing threads. You can create a thread by:
- Using the
Threadclass. - Extending the
Threadclass.
Basic Syntax for Creating a Thread:
import threading
def print_numbers():
for i in range(5):
print(f"Number: {i}")
# Create a thread
thread = threading.Thread(target=print_numbers)
thread.start() # Start the thread
thread.join() # Wait for the thread to finish
3. Creating a Thread by Extending the Thread Class
import threading
class MyThread(threading.Thread):
def run(self):
for i in range(5):
print(f"MyThread: {i}")
# Create and start the thread
thread = MyThread()
thread.start()
thread.join()
4. Multithreading vs Multiprocessing
| Feature | Multithreading | Multiprocessing |
|---|---|---|
| Memory Usage | Threads share the same memory space. | Processes have separate memory spaces. |
| Communication | Faster and easier (shared memory). | Slower (inter-process communication). |
| Use Case | I/O-bound tasks (file I/O, network I/O). | CPU-bound tasks (heavy computations). |
5. Synchronizing Threads
When multiple threads access shared resources, you may encounter race conditions. Python provides synchronization tools like Lock to prevent race conditions.
Example Using Lock:
import threading
counter = 0
lock = threading.Lock()
def increment():
global counter
for _ in range(1000000):
with lock: # Lock the critical section
counter += 1
thread1 = threading.Thread(target=increment)
thread2 = threading.Thread(target=increment)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(f"Final Counter: {counter}")
6. Daemon Threads
A daemon thread runs in the background and automatically terminates when the main thread ends. Use daemon threads for tasks like logging or background cleanup.
Example of Daemon Thread:
import threading
import time
def background_task():
while True:
print("Running background task...")
time.sleep(1)
thread = threading.Thread(target=background_task, daemon=True)
thread.start()
time.sleep(3)
print("Main thread ends.")
7. Thread Communication Using Queue
The queue module provides a thread-safe way to share data between threads.
Example:
import threading
import queue
import time
q = queue.Queue()
def producer():
for i in range(5):
q.put(i)
print(f"Produced: {i}")
time.sleep(1)
def consumer():
while True:
item = q.get()
if item is None:
break
print(f"Consumed: {item}")
producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)
producer_thread.start()
consumer_thread.start()
producer_thread.join()
q.put(None) # Signal consumer to stop
consumer_thread.join()
Conclusion
Python multithreading is a powerful tool for running tasks concurrently, especially in I/O-bound applications. By understanding how to use threads, locks, and thread communication, you can write more efficient and responsive Python programs.