We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← True Parallelism and the Runtime step 17 of 18
Subinterpreters: PEP 734 and the Typed Queue
Subinterpreters are the first genuinely new concurrency substrate since
asyncio: same process, no fork hazards, no second memory image, no __main__
re-import, and a cheaper transport than a pipe.
PEP 684 (3.12) gave each subinterpreter its own GIL. PEP 734 (3.14)
exposes it: concurrent.interpreters, plus
concurrent.futures.InterpreterPoolExecutor — which is a ThreadPoolExecutor
subclass, one OS thread per worker, each thread running its own interpreter.
Verified against 3.14.6: create(), create_queue(), get_current(),
get_main(), list_all(), is_shareable(); and on an Interpreter:
exec, call, call_in_thread, prepare_main, close.
sys.implementation.supports_isolated_interpreters is True.
What crosses, and how
Shareable without copying: None, bool, int, float, bytes, str,
tuples of those, memoryview, and Queue itself.
Everything else — including list and dict — is copied via pickle. So a
dict does arrive, and it arrives as a different object, and if it contains a
lambda you are back in lesson 10.1.
And the __main__ trap is identical to spawn: a function defined in a REPL,
a heredoc or an exec‘d namespace has no importable qualified name, so
handing it to another interpreter cannot work.
Honest numbers
10-core arm64, 8 workers, a pure-Python integer loop:
| Approach | Time | Speed-up |
|---|---|---|
| serial | 516 ms | 1.00x |
ThreadPoolExecutor |
512 ms | 1.01x — the GIL |
ProcessPoolExecutor |
130 ms | 3.96x |
InterpreterPoolExecutor |
121 ms | 4.25x |
A modest, real margin over processes — not a 10x story, and anyone selling you one is selling you something.
Your task
class TypedQueue[T]:
def __init__(self, item_type: type[T], raw: interpreters.Queue) -> None: ...
def put(self, item: T) -> None: ...
def get(self, timeout: int = 5) -> T: ... # isinstance-checked
def run_isolated[T](channel: TypedQueue[T], code: str, **shared: object) -> T: ...
run_isolated creates a fresh interpreter, prepare_mains the queue and any
extra names into it, execs the code, collects the one result, and always
closes the interpreter.
def solve(
*, code: str, values: list[int], failing_code: str
) -> tuple[tuple[int, ...], int, str, bool]:
Return the sorted results, the change in live-interpreter count (which must be
0 — every interpreter you made, you closed), the exception class name that
failing_code produced, and confirmation that a dict survives the queue.
The ordering constraint in run_isolated is the subtle part: read before
you close. An item still sitting in the queue when its sending interpreter is
destroyed becomes unbound, and by default you get a sentinel rather than
your value. Collect, then tear down.
Why the runtime check is not redundant
def get(self, timeout: int = 5) -> T:
item = self._raw.get(timeout=timeout)
if not isinstance(item, self._item_type):
raise TypeError(...)
return item
Everywhere else in this course, an isinstance guard on a value the checker
already knows the type of is noise. Here it is load-bearing: the sender is a
different interpreter, running code the type checker never analysed, and no
static analysis can span that boundary. This is the same category as parsing
JSON off a socket — the boundary is where types are established, not
assumed.
Documentation drift, and the habit it should teach
The published docs describe QueueEmptyError / QueueFullError and
exec(code, /, dedent=True). The shipped module exports QueueEmpty and
QueueFull, and its signature is exec(self, code, /) with no
dedent. For portability, catch queue.Empty and queue.Full — the module’s
exceptions subclass them.
The habit: on a module this new, verify against dir() and inspect.signature
rather than prose. Prose lags the implementation, and a try/except QueueEmptyError that raises AttributeError is a worse failure than the one
you were guarding against.
Loading visualization…
Stuck?
Python reference solution
Sign in to attempt this problem and reveal the reference solution.