Threading in Python enables concurrent execution of code, allowing tasks to run simultaneously within a single program. This is particularly useful for tasks like I/O operations, network requests, and other tasks that can run independently. This tutorial covers everything you need to know about threading in Python, from creating and managing threads to synchronizing tasks.
In Python, threading allows the concurrent execution of code within a program. Using threads, you can split tasks into separate threads of execution that run concurrently. This is especially useful for tasks that may take time to complete, such as file I/O or network requests, without blocking the main program.
Threads are useful in Python for several reasons:
Python’s threading
module provides tools to create and manage threads. You can start by importing the threading
module:
import threading
Python provides two main ways to create threads: using threading.Thread()
directly or subclassing the Thread
class.
threading.Thread()
You can create a thread by passing a function to threading.Thread()
and then starting the thread.
import threading
import time
def print_numbers():
for i in range(5):
print(i)
time.sleep(1)
# Create a thread that runs the print_numbers function
thread = threading.Thread(target=print_numbers)
thread.start()
thread.join() # Wait for the thread to complete
target=print_numbers
specifies the function to run in the new thread.start()
begins the thread execution.join()
ensures that the main program waits for the thread to complete before continuing.Another way to create a thread is by subclassing Thread
and overriding its run()
method.
import threading
import time
class NumberPrinter(threading.Thread):
def run(self):
for i in range(5):
print(i)
time.sleep(1)
# Create an instance of NumberPrinter and start the thread
thread = NumberPrinter()
thread.start()
thread.join()
NumberPrinter
class that inherits from Thread
.run()
method to specify what the thread should do.start()
on an instance runs the overridden run()
method in a new thread.Thread synchronization is essential to avoid conflicts when multiple threads access shared resources.
Locks ensure that only one thread can access a shared resource at a time.
import threading
lock = threading.Lock()
counter = 0
def increment():
global counter
for _ in range(1000):
lock.acquire()
counter += 1
lock.release()
threads = [threading.Thread(target=increment) for _ in range(5)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
print("Final counter:", counter)
lock.acquire()
locks the resource, and lock.release()
unlocks it, preventing other threads from accessing it simultaneously.Semaphores control access to a resource, allowing a set number of threads to access it concurrently.
import threading
import time
semaphore = threading.Semaphore(2)
def access_resource(thread_id):
semaphore.acquire()
print(f"Thread {thread_id} is accessing the resource.")
time.sleep(1)
print(f"Thread {thread_id} is releasing the resource.")
semaphore.release()
threads = [threading.Thread(target=access_resource, args=(i,)) for i in range(5)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
Semaphore(2)
allows only two threads to access the resource at the same time.Event objects allow one or more threads to wait for an event to occur.
import threading
event = threading.Event()
def wait_for_event():
print("Thread waiting for event to be set.")
event.wait()
print("Event has been set, proceeding...")
thread = threading.Thread(target=wait_for_event)
thread.start()
input("Press Enter to set the event: ")
event.set()
event.wait()
blocks until event.set()
is called, which signals all waiting threads to proceed.Communication and coordination between threads help manage complex interactions.
Queues provide a thread-safe way to pass data between threads.
import threading
import queue
q = queue.Queue()
def producer():
for i in range(5):
print("Producing", i)
q.put(i)
def consumer():
while True:
item = q.get()
if item is None:
break
print("Consuming", item)
thread1 = threading.Thread(target=producer)
thread2 = threading.Thread(target=consumer)
thread1.start()
thread2.start()
thread1.join()
q.put(None) # Signal the consumer to exit
thread2.join()
Condition objects allow threads to wait for specific conditions.
import threading
condition = threading.Condition()
data_ready = False
def producer():
global data_ready
with condition:
data_ready = True
print("Data is ready")
condition.notify_all()
def consumer():
with condition:
while not data_ready:
condition.wait()
print("Consuming the data")
thread1 = threading.Thread(target=producer)
thread2 = threading.Thread(target=consumer)
thread2.start()
thread1.start()
thread1.join()
thread2.join()
Daemon threads run in the background and are terminated when the main program exits.
import threading
import time
def background_task():
while True:
print("Background task running")
time.sleep(1)
thread = threading.Thread(target=background_task)
thread.daemon = True # Set the thread as a daemon
thread.start()
time.sleep(3) # Main thread waits for 3 seconds
print("Main thread exiting") # Background thread will stop when main thread exits
daemon = True
makes the thread run in the background and exit with the main program.import threading
import requests
from queue import Queue
urls = [
"https://example.com/page1",
"https://example.com/page2",
"https://example.com/page3",
]
def download_page(url):
response = requests.get(url)
print(f"Downloaded {url} with status code {response.status_code}")
threads = []
for url in urls:
thread = threading.Thread(target=download_page, args=(url,))
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
threading.Thread()
or subclass Thread
to create threads.Threading in Python provides a way to run tasks concurrently, making programs more efficient and responsive. By understanding thread creation, synchronization, and communication, you can effectively manage complex tasks. Threading is an essential tool for any Python programmer working with I/O-bound applications, data processing, or real-time systems.
With Python’s threading module, you can:
Ready to take advantage of threading in Python? Start by creating basic threads, then try implementing synchronization and communication strategies. Happy coding!