2010-02-23 16:58:51 -05:00
|
|
|
|
PEP: 3148
|
|
|
|
|
Title: futures - execute computations asynchronously
|
|
|
|
|
Version: $Revision$
|
|
|
|
|
Last-Modified: $Date$
|
|
|
|
|
Author: Brian Quinlan <brian@sweetapp.com>
|
|
|
|
|
Status: Draft
|
|
|
|
|
Type: Standards Track
|
|
|
|
|
Content-Type: text/x-rst
|
|
|
|
|
Created: 16-Oct-2009
|
|
|
|
|
Python-Version: 3.2
|
|
|
|
|
Post-History:
|
|
|
|
|
|
|
|
|
|
========
|
|
|
|
|
Abstract
|
|
|
|
|
========
|
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
This PEP proposes a design for a package that facilitates the
|
|
|
|
|
evaluation of callables using threads and processes.
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
|
|
|
|
==========
|
|
|
|
|
Motivation
|
|
|
|
|
==========
|
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
Python currently has powerful primitives to construct multi-threaded
|
|
|
|
|
and multi-process applications but parallelizing simple operations
|
|
|
|
|
requires a lot of work i.e. explicitly launching processes/threads,
|
|
|
|
|
constructing a work/results queue, and waiting for completion or some
|
|
|
|
|
other termination condition (e.g. failure, timeout). It is also
|
|
|
|
|
difficult to design an application with a global process/thread limit
|
|
|
|
|
when each component invents its own parallel execution strategy.
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
|
|
|
|
=============
|
|
|
|
|
Specification
|
|
|
|
|
=============
|
|
|
|
|
|
2010-06-28 15:32:11 -04:00
|
|
|
|
Naming
|
|
|
|
|
------
|
|
|
|
|
|
|
|
|
|
The proposed package would be called "futures" and would live in a new
|
|
|
|
|
"concurrent" top-level package. The rationale behind pushing the
|
|
|
|
|
futures library into a "concurrent" namespace has multiple components.
|
|
|
|
|
The first, most simple one is to prevent any and all confusion with
|
|
|
|
|
the existing "from __future__ import x" idiom which has been in use
|
|
|
|
|
for a long time within Python. Additionally, it is felt that adding
|
|
|
|
|
the "concurrent" precursor to the name fully denotes what the library
|
|
|
|
|
is related to - namely concurrency - this should clear up any addition
|
|
|
|
|
ambiguity as it has been noted that not everyone in the community is
|
|
|
|
|
familiar with Java Futures, or the Futures term except as it relates
|
|
|
|
|
to the US stock market.
|
|
|
|
|
|
|
|
|
|
Finally; we are carving out a new namespace for the standard library -
|
|
|
|
|
obviously named "concurrent". In this namespace we hope to either add,
|
|
|
|
|
or move existing, concurrency-related libraries to this in the future.
|
|
|
|
|
A prime example is the multiprocessing.Pool work, as well as other
|
|
|
|
|
"addons" included in that module, which work across thread, and
|
|
|
|
|
process boundaries.
|
|
|
|
|
|
2010-02-23 16:58:51 -05:00
|
|
|
|
Interface
|
|
|
|
|
---------
|
|
|
|
|
|
2010-06-28 15:32:11 -04:00
|
|
|
|
The proposed package provides two core classes: `Executor` and
|
|
|
|
|
`Future`. An `Executor` receives asynchronous work requests (in terms
|
|
|
|
|
of a callable and its arguments) and returns a `Future` to represent
|
|
|
|
|
the execution of that work request.
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
|
|
|
|
Executor
|
|
|
|
|
''''''''
|
|
|
|
|
|
|
|
|
|
`Executor` is an abstract class that provides methods to execute calls
|
|
|
|
|
asynchronously.
|
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
``submit(fn, *args, **kwargs)``
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
Schedules the callable to be executed as ``fn(*args, **kwargs)``
|
|
|
|
|
and returns a `Future` instance representing the execution of the
|
2010-06-28 00:54:01 -04:00
|
|
|
|
callable.
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
This is an abstract method and must be implemented by Executor
|
|
|
|
|
subclasses.
|
2010-02-25 10:42:16 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
``map(func, *iterables, timeout=None)``
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
2010-05-21 18:00:25 -04:00
|
|
|
|
Equivalent to ``map(func, *iterables)`` but func is executed
|
|
|
|
|
asynchronously and several calls to func may be made concurrently.
|
|
|
|
|
The returned iterator raises a `TimeoutError` if `__next__()` is
|
|
|
|
|
called and the result isn't available after *timeout* seconds from
|
|
|
|
|
the original call to `map()`. If *timeout* is not specified or
|
|
|
|
|
`None` then there is no limit to the wait time. If a call raises
|
|
|
|
|
an exception then that exception will be raised when its value is
|
|
|
|
|
retrieved from the iterator.
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
``shutdown(wait=True)``
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
Signal the executor that it should free any resources that it is
|
|
|
|
|
using when the currently pending futures are done executing.
|
|
|
|
|
Calls to `Executor.submit` and `Executor.map` and made after
|
|
|
|
|
shutdown will raise `RuntimeError`.
|
|
|
|
|
|
2010-06-28 00:54:01 -04:00
|
|
|
|
If wait is `True` then this method will not return until all the
|
2010-05-01 05:58:11 -04:00
|
|
|
|
pending futures are done executing and the resources associated
|
2010-06-28 00:54:01 -04:00
|
|
|
|
with the executor have been freed. If wait is `False` then this
|
|
|
|
|
method will return immediately and the resources associated with
|
|
|
|
|
the executor will be freed when all pending futures are done
|
|
|
|
|
executing. Regardless of the value of wait, the entire Python
|
|
|
|
|
program will not exit until all pending futures are done
|
|
|
|
|
executing.
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
| ``__enter__()``
|
|
|
|
|
| ``__exit__(exc_type, exc_val, exc_tb)``
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
When using an executor as a context manager, `__exit__` will call
|
|
|
|
|
``Executor.shutdown(wait=True)``.
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ProcessPoolExecutor
|
|
|
|
|
'''''''''''''''''''
|
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
The `ProcessPoolExecutor` class is an `Executor` subclass that uses a
|
|
|
|
|
pool of processes to execute calls asynchronously. The callable
|
|
|
|
|
objects and arguments passed to `ProcessPoolExecutor.submit` must be
|
2010-06-28 00:54:01 -04:00
|
|
|
|
pickleable according to the same limitations as the multiprocessing
|
2010-05-01 05:58:11 -04:00
|
|
|
|
module.
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
Calling `Executor` or `Future` methods from within a callable
|
|
|
|
|
submitted to a `ProcessPoolExecutor` will result in deadlock.
|
2010-05-01 05:40:47 -04:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
``__init__(max_workers)``
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
Executes calls asynchronously using a pool of a most *max_workers*
|
|
|
|
|
processes. If *max_workers* is ``None`` or not given then as many
|
|
|
|
|
worker processes will be created as the machine has processors.
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
|
|
|
|
ThreadPoolExecutor
|
|
|
|
|
''''''''''''''''''
|
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
The `ThreadPoolExecutor` class is an `Executor` subclass that uses a
|
|
|
|
|
pool of threads to execute calls asynchronously.
|
2010-02-25 10:42:16 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
Deadlock can occur when the callable associated with a `Future` waits
|
|
|
|
|
on the results of another `Future`. For example::
|
2010-02-25 10:42:16 -05:00
|
|
|
|
|
|
|
|
|
import time
|
|
|
|
|
def wait_on_b():
|
|
|
|
|
time.sleep(5)
|
|
|
|
|
print(b.result()) # b will never complete because it is waiting on a.
|
|
|
|
|
return 5
|
|
|
|
|
|
|
|
|
|
def wait_on_a():
|
|
|
|
|
time.sleep(5)
|
|
|
|
|
print(a.result()) # a will never complete because it is waiting on b.
|
|
|
|
|
return 6
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
executor = ThreadPoolExecutor(max_workers=2)
|
|
|
|
|
a = executor.submit(wait_on_b)
|
|
|
|
|
b = executor.submit(wait_on_a)
|
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
And::
|
2010-02-25 10:42:16 -05:00
|
|
|
|
|
|
|
|
|
def wait_on_future():
|
|
|
|
|
f = executor.submit(pow, 5, 2)
|
|
|
|
|
# This will never complete because there is only one worker thread and
|
|
|
|
|
# it is executing this function.
|
|
|
|
|
print(f.result())
|
|
|
|
|
|
|
|
|
|
executor = ThreadPoolExecutor(max_workers=1)
|
|
|
|
|
executor.submit(wait_on_future)
|
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
``__init__(max_workers)``
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
Executes calls asynchronously using a pool of at most
|
|
|
|
|
*max_workers* threads.
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
|
|
|
|
Future Objects
|
|
|
|
|
''''''''''''''
|
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
The `Future` class encapsulates the asynchronous execution of a
|
2010-06-28 00:54:01 -04:00
|
|
|
|
callable. `Future` instances are returned by `Executor.submit`.
|
2010-02-25 10:42:16 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
``cancel()``
|
2010-02-25 10:42:16 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
Attempt to cancel the call. If the call is currently being
|
|
|
|
|
executed then it cannot be cancelled and the method will return
|
|
|
|
|
`False`, otherwise the call will be cancelled and the method will
|
|
|
|
|
return `True`.
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
``cancelled()``
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
Return `True` if the call was successfully cancelled.
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
``running()``
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
Return `True` if the call is currently being executed and cannot
|
|
|
|
|
be cancelled.
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
``done()``
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
Return `True` if the call was successfully cancelled or finished
|
|
|
|
|
running.
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
``result(timeout=None)``
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
2010-07-18 09:12:03 -04:00
|
|
|
|
Return the value returned by the call. If the call hasn't yet
|
2010-05-01 05:58:11 -04:00
|
|
|
|
completed then this method will wait up to *timeout* seconds. If
|
|
|
|
|
the call hasn't completed in *timeout* seconds then a
|
|
|
|
|
`TimeoutError` will be raised. If *timeout* is not specified or
|
|
|
|
|
`None` then there is no limit to the wait time.
|
|
|
|
|
|
|
|
|
|
If the future is cancelled before completing then `CancelledError`
|
|
|
|
|
will be raised.
|
|
|
|
|
|
|
|
|
|
If the call raised then this method will raise the same exception.
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
``exception(timeout=None)``
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
Return the exception raised by the call. If the call hasn't yet
|
|
|
|
|
completed then this method will wait up to *timeout* seconds. If
|
|
|
|
|
the call hasn't completed in *timeout* seconds then a
|
|
|
|
|
`TimeoutError` will be raised. If *timeout* is not specified or
|
|
|
|
|
``None`` then there is no limit to the wait time.
|
|
|
|
|
|
|
|
|
|
If the future is cancelled before completing then `CancelledError`
|
|
|
|
|
will be raised.
|
|
|
|
|
|
|
|
|
|
If the call completed without raising then `None` is returned.
|
2010-05-01 05:40:47 -04:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
``add_done_callback(fn)``
|
2010-05-01 05:40:47 -04:00
|
|
|
|
|
2010-06-28 00:54:01 -04:00
|
|
|
|
Attaches a callable *fn* to the future that will be called when
|
2010-05-01 05:58:11 -04:00
|
|
|
|
the future is cancelled or finishes running. *fn* will be called
|
2010-06-28 00:54:01 -04:00
|
|
|
|
with the future as its only argument. Added callables are called
|
|
|
|
|
in the order that they were added and are always called in a
|
|
|
|
|
thread belonging to the process that added them.
|
2010-05-01 05:58:11 -04:00
|
|
|
|
|
|
|
|
|
If the future has already completed or been cancelled then *fn*
|
2010-06-28 00:54:01 -04:00
|
|
|
|
will be called immediately.
|
2010-05-01 05:40:47 -04:00
|
|
|
|
|
2010-02-25 10:42:16 -05:00
|
|
|
|
Internal Future Methods
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
The following `Future` methods are meant for use in unit tests and
|
|
|
|
|
`Executor` implementations.
|
2010-02-25 10:42:16 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
``set_running_or_notify_cancel()``
|
2010-02-25 10:42:16 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
Should be called by `Executor` implementations before executing
|
|
|
|
|
the work associated with the `Future`.
|
|
|
|
|
|
|
|
|
|
If the method returns `False` then the `Future` was cancelled,
|
|
|
|
|
i.e. `Future.cancel` was called and returned `True`. Any threads
|
|
|
|
|
waiting on the `Future` completing (i.e. through `as_completed()`
|
|
|
|
|
or `wait()`) will be woken up.
|
|
|
|
|
|
|
|
|
|
If the method returns `True` then the `Future` was not cancelled
|
|
|
|
|
and has been put in the running state, i.e. calls to
|
|
|
|
|
`Future.running()` will return `True`.
|
|
|
|
|
|
|
|
|
|
This method can only be called once and cannot be called after
|
|
|
|
|
`Future.set_result()` or `Future.set_exception()` have been
|
|
|
|
|
called.
|
2010-02-25 10:42:16 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
``set_result(result)``
|
2010-02-25 10:42:16 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
Sets the result of the work associated with the `Future`.
|
2010-02-25 10:42:16 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
``set_exception(exception)``
|
2010-02-25 10:42:16 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
Sets the result of the work associated with the `Future` to the
|
|
|
|
|
given `Exception`.
|
2010-02-25 10:42:16 -05:00
|
|
|
|
|
2010-02-23 16:58:51 -05:00
|
|
|
|
Module Functions
|
|
|
|
|
''''''''''''''''
|
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
``wait(fs, timeout=None, return_when=ALL_COMPLETED)``
|
|
|
|
|
|
2010-06-28 00:54:01 -04:00
|
|
|
|
Wait for the `Future` instances (possibly created by different
|
|
|
|
|
`Executor` instances) given by *fs* to complete. Returns a named
|
|
|
|
|
2-tuple of sets. The first set, named "done", contains the
|
|
|
|
|
futures that completed (finished or were cancelled) before the
|
|
|
|
|
wait completed. The second set, named "not_done", contains
|
|
|
|
|
uncompleted futures.
|
2010-05-01 05:58:11 -04:00
|
|
|
|
|
|
|
|
|
*timeout* can be used to control the maximum number of seconds to
|
|
|
|
|
wait before returning. If timeout is not specified or None then
|
|
|
|
|
there is no limit to the wait time.
|
|
|
|
|
|
|
|
|
|
*return_when* indicates when the method should return. It must be
|
|
|
|
|
one of the following constants:
|
|
|
|
|
|
|
|
|
|
============================= ==================================================
|
|
|
|
|
Constant Description
|
|
|
|
|
============================= ==================================================
|
|
|
|
|
`FIRST_COMPLETED` The method will return when any future finishes or
|
|
|
|
|
is cancelled.
|
|
|
|
|
`FIRST_EXCEPTION` The method will return when any future finishes by
|
|
|
|
|
raising an exception. If not future raises an
|
|
|
|
|
exception then it is equivalent to ALL_COMPLETED.
|
|
|
|
|
`ALL_COMPLETED` The method will return when all calls finish.
|
|
|
|
|
============================= ==================================================
|
|
|
|
|
|
|
|
|
|
``as_completed(fs, timeout=None)``
|
|
|
|
|
|
|
|
|
|
Returns an iterator over the `Future` instances given by *fs* that
|
|
|
|
|
yields futures as they complete (finished or were cancelled). Any
|
|
|
|
|
futures that completed before `as_completed()` was called will be
|
|
|
|
|
yielded first. The returned iterator raises a `TimeoutError` if
|
|
|
|
|
`__next__()` is called and the result isn't available after
|
|
|
|
|
*timeout* seconds from the original call to `as_completed()`. If
|
|
|
|
|
*timeout* is not specified or `None` then there is no limit to the
|
|
|
|
|
wait time.
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
2010-06-28 00:54:01 -04:00
|
|
|
|
The `Future` instances can have been created by different
|
|
|
|
|
`Executor` instances.
|
|
|
|
|
|
|
|
|
|
Check Prime Example
|
|
|
|
|
-------------------
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
from concurrent import futures
|
|
|
|
|
import math
|
|
|
|
|
|
|
|
|
|
PRIMES = [
|
|
|
|
|
112272535095293,
|
|
|
|
|
112582705942171,
|
|
|
|
|
112272535095293,
|
|
|
|
|
115280095190773,
|
|
|
|
|
115797848077099,
|
|
|
|
|
1099726899285419]
|
|
|
|
|
|
|
|
|
|
def is_prime(n):
|
|
|
|
|
if n % 2 == 0:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
sqrt_n = int(math.floor(math.sqrt(n)))
|
|
|
|
|
for i in range(3, sqrt_n + 1, 2):
|
|
|
|
|
if n % i == 0:
|
|
|
|
|
return False
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
with futures.ProcessPoolExecutor() as executor:
|
|
|
|
|
for number, prime in zip(PRIMES, executor.map(is_prime,
|
|
|
|
|
PRIMES)):
|
|
|
|
|
print('%d is prime: %s' % (number, prime))
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
main()
|
|
|
|
|
|
|
|
|
|
Web Crawl Example
|
|
|
|
|
-----------------
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
from concurrent import futures
|
|
|
|
|
import urllib.request
|
|
|
|
|
|
|
|
|
|
URLS = ['http://www.foxnews.com/',
|
|
|
|
|
'http://www.cnn.com/',
|
|
|
|
|
'http://europe.wsj.com/',
|
|
|
|
|
'http://www.bbc.co.uk/',
|
|
|
|
|
'http://some-made-up-domain.com/']
|
|
|
|
|
|
|
|
|
|
def load_url(url, timeout):
|
|
|
|
|
return urllib.request.urlopen(url, timeout=timeout).read()
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
with futures.ThreadPoolExecutor(max_workers=5) as executor:
|
|
|
|
|
future_to_url = dict(
|
|
|
|
|
(executor.submit(load_url, url, 60), url)
|
|
|
|
|
for url in URLS)
|
|
|
|
|
|
|
|
|
|
for future in futures.as_completed(future_to_url):
|
|
|
|
|
url = future_to_url[future]
|
2010-07-18 09:12:03 -04:00
|
|
|
|
try:
|
2010-06-28 00:54:01 -04:00
|
|
|
|
print('%r page is %d bytes' % (
|
|
|
|
|
url, len(future.result())))
|
2010-07-18 09:12:03 -04:00
|
|
|
|
except Exception as e:
|
|
|
|
|
print('%r generated an exception: %s' % (
|
|
|
|
|
url, e))
|
2010-06-28 00:54:01 -04:00
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
main()
|
|
|
|
|
|
2010-02-23 16:58:51 -05:00
|
|
|
|
=========
|
|
|
|
|
Rationale
|
|
|
|
|
=========
|
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
The proposed design of this module was heavily influenced by the the
|
|
|
|
|
Java java.util.concurrent package [1]_. The conceptual basis of the
|
|
|
|
|
module, as in Java, is the Future class, which represents the progress
|
|
|
|
|
and result of an asynchronous computation. The Future class makes
|
|
|
|
|
little commitment to the evaluation mode being used e.g. it can be be
|
|
|
|
|
used to represent lazy or eager evaluation, for evaluation using
|
|
|
|
|
threads, processes or remote procedure call.
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
|
|
|
|
Futures are created by concrete implementations of the Executor class
|
2010-05-01 05:58:11 -04:00
|
|
|
|
(called ExecutorService in Java). The reference implementation
|
2010-05-21 18:00:25 -04:00
|
|
|
|
provides classes that use either a process or a thread pool to eagerly
|
2010-05-01 05:58:11 -04:00
|
|
|
|
evaluate computations.
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
|
|
|
|
Futures have already been seen in Python as part of a popular Python
|
2010-05-01 05:58:11 -04:00
|
|
|
|
cookbook recipe [2]_ and have discussed on the Python-3000 mailing
|
|
|
|
|
list [3]_.
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
The proposed design is explicit, i.e. it requires that clients be
|
|
|
|
|
aware that they are consuming Futures. It would be possible to design
|
|
|
|
|
a module that would return proxy objects (in the style of `weakref`)
|
|
|
|
|
that could be used transparently. It is possible to build a proxy
|
|
|
|
|
implementation on top of the proposed explicit mechanism.
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
The proposed design does not introduce any changes to Python language
|
|
|
|
|
syntax or semantics. Special syntax could be introduced [4]_ to mark
|
|
|
|
|
function and method calls as asynchronous. A proxy result would be
|
|
|
|
|
returned while the operation is eagerly evaluated asynchronously, and
|
|
|
|
|
execution would only block if the proxy object were used before the
|
|
|
|
|
operation completed.
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
2010-05-01 05:58:11 -04:00
|
|
|
|
Anh Hai Trinh proposed a simpler but more limited API concept [5]_ and
|
|
|
|
|
the API has been discussed in some detail on stdlib-sig [6]_.
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
2010-07-18 09:12:03 -04:00
|
|
|
|
The proposed design was discussed on the Python-Dev mailing list [7]_.
|
|
|
|
|
Following those discussions, the following changes were made:
|
|
|
|
|
|
|
|
|
|
* The `Executor` class was made into an abstract base class
|
|
|
|
|
* The `Future.remove_done_callback` method was removed due to a lack
|
|
|
|
|
of convincing use cases
|
|
|
|
|
* The `Future.add_done_callback` method was modified to allow the
|
|
|
|
|
same callable to be added many times
|
|
|
|
|
* The `Future` class's mutation methods were better documented to
|
|
|
|
|
indicate that they are private to the `Executor` that created them
|
|
|
|
|
|
2010-02-23 16:58:51 -05:00
|
|
|
|
========================
|
|
|
|
|
Reference Implementation
|
|
|
|
|
========================
|
|
|
|
|
|
2010-07-18 09:12:03 -04:00
|
|
|
|
The reference implementation [8]_ contains a complete implementation
|
2010-05-01 05:58:11 -04:00
|
|
|
|
of the proposed design. It has been tested on Linux and Mac OS X.
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
|
|
|
|
==========
|
|
|
|
|
References
|
|
|
|
|
==========
|
|
|
|
|
|
|
|
|
|
.. [1]
|
|
|
|
|
`java.util.concurrent` package documentation
|
2010-07-18 09:12:03 -04:00
|
|
|
|
http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/package-summary.html
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
|
|
|
|
.. [2]
|
|
|
|
|
Python Cookbook recipe 84317, "Easy threading with Futures"
|
2010-07-18 09:12:03 -04:00
|
|
|
|
http://code.activestate.com/recipes/84317/
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
|
|
|
|
.. [3]
|
|
|
|
|
`Python-3000` thread, "mechanism for handling asynchronous concurrency"
|
2010-07-18 09:12:03 -04:00
|
|
|
|
http://mail.python.org/pipermail/python-3000/2006-April/000960.html
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
|
|
|
|
.. [4]
|
|
|
|
|
`Python 3000` thread, "Futures in Python 3000 (was Re: mechanism for handling asynchronous concurrency)"
|
2010-07-18 09:12:03 -04:00
|
|
|
|
http://mail.python.org/pipermail/python-3000/2006-April/000970.html
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
|
|
|
|
.. [5]
|
|
|
|
|
A discussion of `stream`, a similar concept proposed by Anh Hai Trinh
|
2010-07-18 09:12:03 -04:00
|
|
|
|
http://www.mail-archive.com/stdlib-sig@python.org/msg00480.html
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
|
|
|
|
.. [6]
|
2010-05-01 05:40:47 -04:00
|
|
|
|
A discussion of the proposed API on stdlib-sig
|
2010-07-18 09:12:03 -04:00
|
|
|
|
http://mail.python.org/pipermail/stdlib-sig/2009-November/000731.html
|
2010-05-01 05:40:47 -04:00
|
|
|
|
|
|
|
|
|
.. [7]
|
2010-07-18 09:12:03 -04:00
|
|
|
|
A discussion of the PEP on python-dev
|
|
|
|
|
http://mail.python.org/pipermail/python-dev/2010-March/098169.html
|
|
|
|
|
|
|
|
|
|
.. [8]
|
2010-02-25 10:42:16 -05:00
|
|
|
|
Reference `futures` implementation
|
2010-07-18 09:12:03 -04:00
|
|
|
|
http://code.google.com/p/pythonfutures/source/browse/#svn/branches/feedback
|
2010-02-23 16:58:51 -05:00
|
|
|
|
|
|
|
|
|
=========
|
|
|
|
|
Copyright
|
|
|
|
|
=========
|
|
|
|
|
|
|
|
|
|
This document has been placed in the public domain.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
..
|
|
|
|
|
Local Variables:
|
|
|
|
|
mode: indented-text
|
|
|
|
|
indent-tabs-mode: nil
|
|
|
|
|
sentence-end-double-space: t
|
|
|
|
|
fill-column: 70
|
|
|
|
|
coding: utf-8
|
|
|
|
|
End:
|