Python’s threading module creates and manages threads for concurrent work within one process. Threads share the process’s memory, while the global interpreter lock allows only one thread to execute Python bytecode at a time. section 7.1 section 1.4.2
Use threading when the work benefits from overlapping waits or coordinating shared state:
- Run blocking I/O concurrently because operations such as socket recv and sendall release the GIL while waiting. section 7.1
- Create a Thread with a target function and arguments, call start to begin it, and call join when the caller must wait for completion. section 7.1 section 1.4.2
- Protect shared variables with threading.Lock, acquiring and releasing it around critical sections or using it as a context manager. section 7.3
- Scale repeated blocking tasks with thread pool executors and combine them with asyncio when asynchronous code must await thread results. section 7.6
Thread-per-connection echo server
A main thread accepts socket connections and starts one Thread for each client. Each worker runs echo, repeatedly receiving and sending data, allowing multiple blocking clients to proceed concurrently. section 7.1
Listing 7.1 A multithreaded echo server
from threading import Thread
import socket
def echo(client: socket):
while True:
data = client.recv(2048)
print(f'Received {data}, sending!')
client.sendall(data)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(('127.0.0.1', 8000))
server.listen()
while True:
connection, _ = server.accept() #1
thread = Thread(target=echo, args=(connection,)) #2
thread.start() #3
Inspect the runnable echo-server listing showing Thread construction with a target and args, followed by start. section 7.1
