sequenceDiagram participant Main as Main Thread participant Exec as ThreadPoolExecutor participant W1 as Worker Thread 1 participant W2 as Worker Thread 2 participant W3 as Worker Thread 3 participant Q as Internal...
Shreedhar Kodate·12 July 2025·5 min read
python-engg-tmplatesdocs
1. Thread Pool Execution Flow
sequenceDiagram
participant Main as Main Thread
participant Exec as ThreadPoolExecutor
participant W1 as Worker Thread 1
participant W2 as Worker Thread 2
participant W3 as Worker Thread 3
participant Q as Internal Work Queue
Main->>Exec: __enter__() – create pool (max_workers=N)
Exec->>W1: spawn thread
Exec->>W2: spawn thread
Exec->>W3: spawn thread
loop For each task
Main->>Exec: submit(fn, arg_i)
Exec->>Q: enqueue Future
end
Q->>W1: dequeue task-0
Q->>W2: dequeue task-1
Q->>W3: dequeue task-2
Note over W1,W3: Threads execute concurrently
W1-->>Main: future-0.set_result()
W3-->>Main: future-2.set_result()
W2-->>Main: future-1.set_result()
Note over Main: as_completed() yields futures in\ncompletion order (not submission order)
Main->>Exec: __exit__() – join all threads
Key points:
as_completed() yields futures as they finish, allowing progressive result
processing without waiting for the slowest task.
Worker threads are reused across tasks (no thread creation overhead per task).
max_workers controls the concurrency limit; set to cpu_count() for
CPU-bound, higher for I/O-bound workloads.
2. Asyncio Event Loop Flow
flowchart TD
subgraph "Event Loop (single OS thread)"
EL([Event Loop starts])
RQ[Ready Queue\ncoroutines to run]
IO[I/O Selector\nepoll / kqueue]
CB[Callback Queue\nI/O completions]
EL --> RQ
RQ -->|"pop next coroutine"| Run[Resume coroutine\nat await point]
Run -->|"hits await asyncio.sleep\nor await network"| Suspend[Suspend coroutine\nregister with selector]
Suspend --> IO
IO -->|"I/O ready"| CB
CB -->|"schedule resumption"| RQ
Run -->|"coroutine returns"| Done[Result available\nfuture.set_result()]
end
App["asyncio.gather(\n coro_1(), coro_2(), ...\n)"] --> EL
Done --> App
Cooperative multitasking: A coroutine runs until it hits an await
expression. At that point it suspends and the event loop picks the next
ready coroutine. This is why asyncio can handle thousands of concurrent
I/O operations with a single OS thread – no context-switch overhead.
asyncio.TaskGroup (Python 3.11+):
sequenceDiagram
participant Main
participant TG as TaskGroup
participant T1 as Task 1
participant T2 as Task 2
participant T3 as Task 3
Main->>TG: async with TaskGroup() as tg:
Main->>TG: tg.create_task(coro1)
TG->>T1: schedule
Main->>TG: tg.create_task(coro2)
TG->>T2: schedule
Main->>TG: tg.create_task(coro3)
TG->>T3: schedule
T1-->>TG: done
T3-->>TG: done
T2-->>TG: done
Note over TG: All tasks done → exit __aexit__
TG-->>Main: results via task.result()
If any task raises an exception, TaskGroup cancels the remaining tasks
and propagates the error as an ExceptionGroup.
Sentinel re-queueing pattern: When a consumer receives None (the
sentinel), it immediately puts None back before exiting. This ensures
every consumer eventually receives the termination signal, regardless of
how many consumers share the queue.
6. Shared Memory Flow (multiprocessing)
flowchart LR
subgraph Main ["Main Process"]
SM["SharedMemory.create()\nshm.name='psm_abc123'"]
Write["Write array → shm.buf"]
end
subgraph Workers ["Worker Processes (spawn)"]
W1["Process 1\nSharedMemory(name='psm_abc123')\nread slice [0:500]"]
W2["Process 2\nSharedMemory(name='psm_abc123')\nread slice [500:1000]"]
end
subgraph Collect ["Result Collection"]
Pool["Pool.map() collects\npartial sums"]
Total["sum(partial_sums)"]
end
SM --> Write
Write -->|"OS shared page"| W1
Write -->|"OS shared page"| W2
W1 -->|"partial_sum_1"| Pool
W2 -->|"partial_sum_2"| Pool
Pool --> Total
Zero-copy: Worker processes map the same physical memory pages. No
serialisation (pickle) occurs for the shared buffer – only the small result
values are pickled back to the main process.