Loky - Robust Process Pool Executor
Loky provides a robust, cross-platform, and cross-version implementation of Python's `concurrent.futures.ProcessPoolExecutor`. It enhances multiprocessing by offering reusable executors, transparent `cloudpickle` integration for complex object serialization, and deadlock-free process management, addressing common pitfalls in parallel Python computing. The library is actively maintained, with its current version being 3.5.6. It primarily follows a minor release cadence with bug fixes and improvements.
Warnings
- breaking Loky's `set_start_method` is incompatible with `multiprocessing.set_start_method`. Attempting to use the standard library's function will not configure Loky's process startup correctly and can lead to unexpected behavior or errors.
- gotcha When using `loky.get_reusable_executor()` on Windows, worker processes are kept alive for reuse, which can prevent cleanup operations (e.g., `os.chdir()` or deleting temporary directories) if they were used within the worker context. Even `loky.ProcessPoolExecutor()` may leave one process active after explicit shutdown.
- gotcha While `loky` transparently integrates `cloudpickle` to serialize non-picklable objects, this serialization can introduce performance overhead compared to Python's standard `pickle` module, especially for very large objects or high-frequency task submission.
- gotcha Loky on POSIX systems (e.g., Linux, macOS) uses `fork+exec` for all processes to ensure consistent and robust spawn behavior, which is safer when interacting with third-party libraries (e.g., OpenMP, macOS Accelerate) compared to `multiprocessing.Pool`'s default `fork` (or pre-Python 3.8 macOS `fork`). This difference might subtly alter behavior if your code relies on `fork` without `exec` semantics.
Install
-
pip install loky
Imports
- get_reusable_executor
from loky import get_reusable_executor
- ProcessPoolExecutor
from loky import ProcessPoolExecutor
- set_start_method
from loky import set_start_method
Quickstart
import os
from loky import get_reusable_executor
def worker_function(x):
# Simulate some work
pid = os.getpid()
return f"Processed {x} by PID {pid}"
if __name__ == "__main__":
# Using get_reusable_executor for managed process pool
with get_reusable_executor(max_workers=2) as executor:
results = list(executor.map(worker_function, range(5)))
print(results)
# Direct ProcessPoolExecutor usage (similar to concurrent.futures)
from loky import ProcessPoolExecutor
with ProcessPoolExecutor(max_workers=2) as executor:
results_direct = list(executor.map(worker_function, range(5, 10)))
print(results_direct)