Home/Python/Futures and executors

Futures and executors

Submit work, retrieve results, and propagate failures

Submitting work

An executor owns a worker pool. submit() schedules a callable and returns a Future representing pending, running, completed, or cancelled work.

future.result() waits if needed, then returns the value or re-raises the callable's exception in the requesting thread. A timeout limits the wait, not the operation's lifetime:

Python
from concurrent.futures import ThreadPoolExecutor

with ThreadPoolExecutor(max_workers=2) as executor:
    valid = executor.submit(int, "42")
    invalid = executor.submit(int, "many")
    print(valid.result())  # 42
    try:
        invalid.result()
    except ValueError:
        print("conversion failed")

Explicit retrieval determines display order; tasks may finish in either order. Calling result() again observes the stored outcome without resubmitting. Exiting the executor's context waits for submitted work.

Ordering and cancellation

executor.map yields results in input order; as_completed exposes completion order. Ordered retrieval can hold a fast result behind a slow earlier item. Retain indices when collecting by completion but presenting by input order.

Cancellation can prevent pending work from starting; it cannot stop a running callable. Events make this distinction deterministic:

Python
from concurrent.futures import ThreadPoolExecutor
from threading import Event

entered, release = Event(), Event()
def wait_for_release():
    entered.set()
    release.wait()
    return "done"

with ThreadPoolExecutor(max_workers=1) as executor:
    active = executor.submit(wait_for_release)
    try:
        entered.wait()
        queued = executor.submit(str, "never run")
        print(queued.cancel(), active.cancel())  # True False
    finally:
        release.set()
    print(active.result())  # done

The first call occupies the sole worker, leaving the second pending. Releasing the first uses a separate signal; cancellation did not interrupt it. Running calls need cooperative stop checks and appropriate timeouts on external operations.

Capacity and failure policy

Thread pools overlap blocking I/O; process pools provide isolation for suitable CPU work. Set worker capacity according to downstream limits and measurement.

Worker count does not bound pending-submission memory. Older Executor.map APIs collect inputs eagerly; submission controls vary by version. Use a window of futures or a bounded producer-consumer queue when pending work must be bounded.

Gotchas

  • One task failing does not cancel its siblings automatically. Define whether to collect item failures or fail the batch.
  • Waiting inside every worker for more work on the same exhausted pool can deadlock.
  • Context-manager shutdown can wait for slow running calls even after the body raises.
  • An executor neither synchronizes application state nor undoes side effects.

Native thread pools are unavailable in the browser runner.

Put it into practice

Implement the exercise in the workspace, then download it and run the tests with your local toolchain.

Open practice
Next lessonProcesses and isolation