Working with subinterpreters

Subinterpreters offer a middle ground between worker threads and worker processes. They allow you to utilize multiple CPU cores to run Python code while avoiding the overhead and complexities of spawning subprocesses.

Warning

Subinterpreter support is considered experimental. The underlying Python API for managing subinterpreters has not been finalized yet, and has had little real-world testing. As such, it is not recommended to use this feature for anything important yet.

Running a function in a worker interpreter

Running functions in a worker interpreter makes sense when:

  • The code you want to run in parallel is CPU intensive

  • The code is either pure Python code, or extension code that does not release the Global Interpreter Lock (GIL)

If the code you’re trying to run only does blocking network I/O, or file I/O, then you’re better off using worker thread instead.

This is done by using interpreter.run_sync():

import time

from anyio import run, to_interpreter

from yourothermodule import cpu_intensive_function

async def main():
    result = await to_interpreter.run_sync(
        cpu_intensive_function, 'Hello, ', 'world!'
    )
    print(result)

run(main)

Limitations

  • Subinterpreters are only supported on Python 3.13 or later

  • Code in the __main__ module cannot be run with this (as a consequence, this applies to any functions defined in the REPL)

  • The target functions cannot react to cancellation

  • Unlike with threads, the code running in the subinterpreter cannot share mutable data with other interpreters/threads (however, sharing _immutable_ data is fine)