Python threading Module

🎓 Top 15 Udemy Courses (80-90% Discount): My Udemy Courses - Ramesh Fadatare — All my Udemy courses are real-time and project oriented courses.

▶️ Subscribe to My YouTube Channel (178K+ subscribers): Java Guides on YouTube

▶️ For AI, ChatGPT, Web, Tech, and Generative AI, subscribe to another channel: Ramesh Fadatare on YouTube

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

  1. Introduction
  2. Key Classes and Functions
    • Thread
    • Lock
    • RLock
    • Condition
    • Semaphore
    • Event
    • Timer
    • Barrier
    • active_count()
    • current_thread()
    • main_thread()
    • enumerate()
  3. Examples
    • Creating and Starting Threads
    • Using Locks
    • Using Condition Variables
    • Using Semaphores
    • Using Events
    • Using Timers
    • Using Barriers
  4. Real-World Use Case
  5. Conclusion
  6. 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.

References

My Top and Bestseller Udemy Courses. The sale is going on with a 70 - 80% discount. The discount coupon has been added to each course below:

Comments

Spring Boot 3 Paid Course Published for Free
on my Java Guides YouTube Channel

Subscribe to my YouTube Channel (165K+ subscribers):
Java Guides Channel

Top 10 My Udemy Courses with Huge Discount:
Udemy Courses - Ramesh Fadatare