The threading
module in Python provides a way to create and manage multiple threads, enabling parallel execution of code. This is particularly useful for I/O-bound and high-level structured network code.
Table of Contents
- Introduction
- Key Classes and Functions
Thread
Lock
RLock
Condition
Semaphore
Event
Timer
Barrier
active_count()
current_thread()
main_thread()
enumerate()
- Examples
- Creating and Starting Threads
- Using Locks
- Using Condition Variables
- Using Semaphores
- Using Events
- Using Timers
- Using Barriers
- Real-World Use Case
- Conclusion
- References
Introduction
The threading
module provides a higher-level interface for working with threads in Python. It includes several classes and functions to manage threads and synchronize their execution.
Key Classes and Functions
Thread
Represents a thread of execution.
import threading
def worker():
print("Worker thread")
thread = threading.Thread(target=worker)
thread.start()
thread.join()
Output:
Worker thread
Lock
A lock object is a synchronization primitive.
import threading
lock = threading.Lock()
lock.acquire()
try:
# Critical section
pass
finally:
lock.release()
RLock
A reentrant lock is a synchronization primitive that may be acquired multiple times by the same thread.
import threading
rlock = threading.RLock()
rlock.acquire()
try:
# Critical section
pass
finally:
rlock.release()
Condition
A condition variable allows one or more threads to wait until they are notified by another thread.
import threading
condition = threading.Condition()
def consumer():
with condition:
condition.wait()
print("Consumer notified")
def producer():
with condition:
print("Producer notifying")
condition.notify_all()
threading.Thread(target=consumer).start()
threading.Thread(target=producer).start()
Output:
Producer notifying
Consumer notified
Semaphore
A semaphore is a synchronization primitive that manages an internal counter.
import threading
semaphore = threading.Semaphore(2)
def worker():
with semaphore:
print("Worker acquired semaphore")
# Critical section
for _ in range(4):
threading.Thread(target=worker).start()
Output:
Worker acquired semaphore
Worker acquired semaphore
Worker acquired semaphore
Worker acquired semaphore
Event
An event object manages an internal flag that threads can wait for.
import threading
event = threading.Event()
def worker():
event.wait()
print("Worker activated")
threading.Thread(target=worker).start()
print("Main thread setting event")
event.set()
Output:
Main thread setting event
Worker activated
Timer
A timer thread executes a function after a specified interval.
import threading
def timeout():
print("Timeout reached")
timer = threading.Timer(2.0, timeout)
timer.start()
Output:
Timeout reached
Barrier
A barrier is a synchronization primitive that allows multiple threads to wait until they have all reached a certain point.
import threading
barrier = threading.Barrier(3)
def worker():
print("Worker waiting at barrier")
barrier.wait()
print("Worker passed barrier")
for _ in range(3):
threading.Thread(target=worker).start()
Output:
Worker waiting at barrier
Worker waiting at barrier
Worker waiting at barrier
Worker passed barrier
Worker passed barrier
Worker passed barrier
active_count()
Returns the number of currently active threads.
import threading
print(f'Active threads: {threading.active_count()}')
Output:
Active threads: 1
current_thread()
Returns the current thread object.
import threading
print(f'Current thread: {threading.current_thread().name}')
Output:
Current thread: MainThread
main_thread()
Returns the main thread object.
import threading
print(f'Main thread: {threading.main_thread().name}')
Output:
Main thread: MainThread
enumerate()
Returns a list of all currently active thread objects.
import threading
threads = threading.enumerate()
print(f'All active threads: {[thread.name for thread in threads]}')
Output:
All active threads: ['MainThread']
Examples
Creating and Starting Threads
import threading
def worker():
print(f'Worker: {threading.current_thread().name}')
threads = []
for i in range(5):
thread = threading.Thread(target=worker, name=f'Thread-{i}')
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
Output:
Worker: Thread-0
Worker: Thread-1
Worker: Thread-2
Worker: Thread-3
Worker: Thread-4
Using Locks
import threading
lock = threading.Lock()
counter = 0
def increment():
global counter
with lock:
local_counter = counter
local_counter += 1
counter = local_counter
print(f'Counter: {counter}')
threads = []
for _ in range(10):
thread = threading.Thread(target=increment)
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
Output:
Counter: 1
Counter: 2
Counter: 3
Counter: 4
Counter: 5
Counter: 6
Counter: 7
Counter: 8
Counter: 9
Counter: 10
Using Condition Variables
import threading
condition = threading.Condition()
items = []
def consumer():
with condition:
while not items:
condition.wait()
item = items.pop(0)
print(f'Consumer got: {item}')
def producer():
with condition:
items.append('item')
print('Producer added item')
condition.notify()
threading.Thread(target=consumer).start()
threading.Thread(target=producer).start()
Output:
Producer added item
Consumer got: item
Using Semaphores
import threading
semaphore = threading.Semaphore(2)
def worker():
with semaphore:
print(f'{threading.current_thread().name} acquired semaphore')
import time
time.sleep(1)
threads = []
for i in range(4):
thread = threading.Thread(target=worker, name=f'Worker-{i}')
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
Output:
Worker-0 acquired semaphore
Worker-1 acquired semaphore
Worker-2 acquired semaphore
Worker-3 acquired semaphore
Using Events
import threading
event = threading.Event()
def worker():
print(f'{threading.current_thread().name} waiting for event')
event.wait()
print(f'{threading.current_thread().name} event triggered')
thread = threading.Thread(target=worker, name='Worker')
thread.start()
import time
time.sleep(2)
print('Main thread setting event')
event.set()
Output:
Worker waiting for event
Main thread setting event
Worker event triggered
Using Timers
import threading
def timeout():
print('Timeout reached')
timer = threading.Timer(2.0, timeout)
timer.start()
timer.join()
Output:
Timeout reached
Using Barriers
import threading
barrier = threading.Barrier(3)
def worker():
print(f'{threading.current_thread().name} waiting at barrier')
barrier.wait()
print(f'{threading.current_thread().name} passed barrier')
threads = []
for i in range(3):
thread = threading.Thread(target=worker, name=f'Worker-{i}')
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
Output:
Worker-0 waiting at barrier
Worker-1 waiting at barrier
Worker-2 waiting at barrier
Worker-2 passed barrier
Worker-0 passed barrier
Worker-1 passed barrier
Real-World Use Case
Web Scraping with Multiple Threads
import threading
import requests
urls = [
'http://example.com',
'http://example.org',
'http://example.net',
]
def fetch(url):
response = requests.get(url)
print(f'Fetched {url} with status {response.status_code}')
threads = []
for url in urls:
thread = threading.Thread(target=fetch, args=(url,))
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
Output:
Fetched http://example.org with status 200
Fetched http://example.com with status 200
Fetched http://example.net with status 200
Conclusion
The threading
The thread module in Python provides a robust and flexible way to work with threads. Threads allow you to execute multiple operations concurrently, which is particularly useful for I/O-bound and network-bound tasks. The module includes various synchronization primitives such as locks, conditions, semaphores, events, and barriers, allowing for effective thread management and coordination.