liveBook · Manning

Thread

As explained in Grokking Concurrency

A thread is an independently schedulable sequence of execution within a process that typically shares the process's memory and resources.

Imagine one construction crew working from several task lists. The workers share tools and a workspace, but each list describes a separate job. Threads work similarly: each is an independent sequence of instructions that the operating system can schedule. A process holds the resources; its threads do the executing. Every thread belongs to a process, and a process has at least one thread. section 4.6 section 4.9

Threads in one process share its address space, files, connections, and data while maintaining their own execution state. The operating system manages them and can schedule them across available cores. On one core, it can instead alternate their execution: concurrency does not require several tasks to run at exactly the same instant. section 4.6 section 6.12

That shared workspace brings trade-offs:

  • Creating, switching between, and communicating through threads generally costs less than doing so through separate processes. section 4.7 section 6.12
  • A change to shared data is visible to other threads. Access to shared resources therefore needs synchronization to avoid corruption; a faulty thread can affect its companions. section 4.7
  • More threads do not guarantee more speed. Choose a concurrency approach for the workload and runtime; the Python password-cracking example uses processes because of a limitation on using threads. section 5.5

Five workers in one process

A Python program starts with a main thread. It creates five more with threading.Thread(target=..., args=...) and calls start() on each. While they are active, threading.active_count() reports six threads, all within the same process. The example demonstrates how to launch separate tasks, not that every workload will finish faster. section 4.8

# Chapter 4/multithreading.py
import os
import time
import threading
from threading import Thread

def cpu_waster(i: int) -> None:
    name = threading.current_thread().getName()
    print(f"{name} doing {i} work")
    time.sleep(3)
        
def display_threads() -> None:
    print("-" * 10)
    print(f"Current process PID: {os.getpid()}")
    print(f"Thread Count: {threading.active_count()}")
    print("Active threads:")
    for thread in threading.enumerate():
        print(thread)
                
def main(num_threads: int) -> None:
    display_threads()                                            #1

    print(f"Starting {num_threads} CPU wasters...")
    for i in range(num_threads):
        thread = Thread(target=cpu_waster, args=(i,))            #2
        thread.start()                                           #2

    display_threads()                                            #1
        
if __name__ == "__main__":
    num_threads = 5
    main(num_threads)

Inspect the Python example to see thread creation, starting, and enumeration alongside the process ID. section 4.8

One server, several clients

A listening server can accept connections in its main thread and give each client a separate handler thread. The operating system overlaps the handlers, so one client's conversation need not be the only work in progress. This is a simple model, though each additional thread has a cost. section 10.6

Threads can also serve behind higher-level patterns. A thread pool can run a long operation while an event loop continues handling other work and a future represents the pending result. Prefer such abstractions when they spare you from managing low-level threads directly. section 12.10 section 4.8

How Grokking Concurrency covers it

How deep it goes
Defines threads through process resources and OS scheduling, then works through Python creation, shared-memory trade-offs, and server use.
Examples
Uses construction crews, five Python child threads, an arcade game's tasks, and a thread-per-client server.
What it assumes
Builds from running programs, process resources, and CPU cores before introducing thread creation; assumes no prior concurrency terminology.
What you can do after
Apply it via Python threading.Thread to launch tasks and implement a thread-per-client server while accounting for shared-resource risks.
How it connects
Links threads to processes, shared memory, synchronization, preemptive multitasking, thread pools, event loops, and futures.

Also explained in

Compare how 12 books explain thread →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Grokking Concurrency. The section links point to the parts of the book it draws on.