This PEP proposes a design for a package that facilitates theevaluation of callables using threads and processes.
Python currently has powerful primitives to construct multi-threadedand multi-process applications but parallelizing simple operationsrequires a lot of work i.e. explicitly launching processes/threads,constructing a work/results queue, and waiting for completion or someother termination condition (e.g. failure, timeout). It is alsodifficult to design an application with a global process/thread limitwhen each component invents its own parallel execution strategy.
The proposed package would be called “futures” and would live in a new“concurrent” top-level package. The rationale behind pushing thefutures library into a “concurrent” namespace has multiple components.The first, most simple one is to prevent any and all confusion withthe existing “from __future__ import x” idiom which has been in usefor a long time within Python. Additionally, it is felt that addingthe “concurrent” precursor to the name fully denotes what the libraryis related to - namely concurrency - this should clear up any additionambiguity as it has been noted that not everyone in the community isfamiliar with Java Futures, or the Futures term except as it relatesto the US stock market.
Finally; we are carving out a new namespace for the standard library -obviously named “concurrent”. We hope to either add, or move existing,concurrency-related libraries to this in the future. A prime exampleis the multiprocessing.Pool work, as well as other “addons” includedin that module, which work across thread and process boundaries.
The proposed package provides two core classes:Executor andFuture. AnExecutor receives asynchronous work requests (in termsof a callable and its arguments) and returns aFuture to representthe execution of that work request.
Executor is an abstract class that provides methods to execute callsasynchronously.
submit(fn,*args,**kwargs)
Schedules the callable to be executed asfn(*args,**kwargs)and returns aFutureinstance representing the execution of thecallable.This is an abstract method and must be implemented by Executorsubclasses.
map(func,*iterables,timeout=None)
Equivalent tomap(func,*iterables)but func is executedasynchronously and several calls to func may be made concurrently.The returned iterator raises aTimeoutErrorif__next__()iscalled and the result isn’t available aftertimeout seconds fromthe original call tomap(). Iftimeout is not specified orNonethen there is no limit to the wait time. If a call raisesan exception then that exception will be raised when its value isretrieved from the iterator.
shutdown(wait=True)
Signal the executor that it should free any resources that it isusing when the currently pending futures are done executing.Calls toExecutor.submitandExecutor.mapand made aftershutdown will raiseRuntimeError.If wait is
Truethen this method will not return until all thepending futures are done executing and the resources associatedwith the executor have been freed. If wait isFalsethen thismethod will return immediately and the resources associated withthe executor will be freed when all pending futures are doneexecuting. Regardless of the value of wait, the entire Pythonprogram will not exit until all pending futures are doneexecuting.
__enter__()__exit__(exc_type,exc_val,exc_tb)When using an executor as a context manager,__exit__will callExecutor.shutdown(wait=True).
TheProcessPoolExecutor class is anExecutor subclass that uses apool of processes to execute calls asynchronously. The callableobjects and arguments passed toProcessPoolExecutor.submit must bepickleable according to the same limitations as the multiprocessingmodule.
CallingExecutor orFuture methods from within a callablesubmitted to aProcessPoolExecutor will result in deadlock.
__init__(max_workers)
Executes calls asynchronously using a pool of a mostmax_workersprocesses. Ifmax_workers isNoneor not given then as manyworker processes will be created as the machine has processors.
TheThreadPoolExecutor class is anExecutor subclass that uses apool of threads to execute calls asynchronously.
Deadlock can occur when the callable associated with aFuture waitson the results of anotherFuture. For example:
importtimedefwait_on_b():time.sleep(5)print(b.result())# b will never complete because it is waiting on a.return5defwait_on_a():time.sleep(5)print(a.result())# a will never complete because it is waiting on b.return6executor=ThreadPoolExecutor(max_workers=2)a=executor.submit(wait_on_b)b=executor.submit(wait_on_a)
And:
defwait_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)
__init__(max_workers)
Executes calls asynchronously using a pool of at mostmax_workers threads.
TheFuture class encapsulates the asynchronous execution of acallable.Future instances are returned byExecutor.submit.
cancel()
Attempt to cancel the call. If the call is currently beingexecuted then it cannot be cancelled and the method will returnFalse, otherwise the call will be cancelled and the method willreturnTrue.
cancelled()
ReturnTrueif the call was successfully cancelled.
running()
ReturnTrueif the call is currently being executed and cannotbe cancelled.
done()
ReturnTrueif the call was successfully cancelled or finishedrunning.
result(timeout=None)
Return the value returned by the call. If the call hasn’t yetcompleted then this method will wait up totimeout seconds. Ifthe call hasn’t completed intimeout seconds then aTimeoutErrorwill be raised. Iftimeout is not specified orNonethen there is no limit to the wait time.If the future is cancelled before completing then
CancelledErrorwill be raised.If the call raised then this method will raise the same exception.
exception(timeout=None)
Return the exception raised by the call. If the call hasn’t yetcompleted then this method will wait up totimeout seconds. Ifthe call hasn’t completed intimeout seconds then aTimeoutErrorwill be raised. Iftimeout is not specified orNonethen there is no limit to the wait time.If the future is cancelled before completing then
CancelledErrorwill be raised.If the call completed without raising then
Noneis returned.
add_done_callback(fn)
Attaches a callablefn to the future that will be called whenthe future is cancelled or finishes running.fn will be calledwith the future as its only argument.Added callables are called in the order that they were added andare always called in a thread belonging to the process that addedthem. If the callable raises an
Exceptionthen it will belogged and ignored. If the callable raises anotherBaseExceptionthen behavior is not defined.If the future has already completed or been cancelled thenfnwill be called immediately.
The followingFuture methods are meant for use in unit tests andExecutor implementations.
set_running_or_notify_cancel()
Should be called byExecutorimplementations before executingthe work associated with theFuture.If the method returns
Falsethen theFuturewas cancelled,i.e.Future.cancelwas called and returnedTrue. Any threadswaiting on theFuturecompleting (i.e. throughas_completed()orwait()) will be woken up.If the method returns
Truethen theFuturewas not cancelledand has been put in the running state, i.e. calls toFuture.running()will returnTrue.This method can only be called once and cannot be called after
Future.set_result()orFuture.set_exception()have beencalled.
set_result(result)
Sets the result of the work associated with theFuture.
set_exception(exception)
Sets the result of the work associated with theFutureto thegivenException.
wait(fs,timeout=None,return_when=ALL_COMPLETED)
Wait for theFutureinstances (possibly created by differentExecutorinstances) given byfs to complete. Returns a named2-tuple of sets. The first set, named “done”, contains thefutures that completed (finished or were cancelled) before thewait completed. The second set, named “not_done”, containsuncompleted futures.timeout can be used to control the maximum number of seconds towait before returning. If timeout is not specified or None thenthere is no limit to the wait time.
return_when indicates when the method should return. It must beone of the following constants:
Constant Description FIRST_COMPLETEDThe method will return when any future finishes oris cancelled. FIRST_EXCEPTIONThe method will return when any future finishes byraising an exception. If not future raises anexception then it is equivalent to ALL_COMPLETED. ALL_COMPLETEDThe method will return when all calls finish.
as_completed(fs,timeout=None)
Returns an iterator over theFutureinstances given byfs thatyields futures as they complete (finished or were cancelled). Anyfutures that completed beforeas_completed()was called will beyielded first. The returned iterator raises aTimeoutErrorif__next__()is called and the result isn’t available aftertimeout seconds from the original call toas_completed(). Iftimeout is not specified orNonethen there is no limit to thewait time.The
Futureinstances can have been created by differentExecutorinstances.
fromconcurrentimportfuturesimportmathPRIMES=[112272535095293,112582705942171,112272535095293,115280095190773,115797848077099,1099726899285419]defis_prime(n):ifn%2==0:returnFalsesqrt_n=int(math.floor(math.sqrt(n)))foriinrange(3,sqrt_n+1,2):ifn%i==0:returnFalsereturnTruedefmain():withfutures.ProcessPoolExecutor()asexecutor:fornumber,primeinzip(PRIMES,executor.map(is_prime,PRIMES)):print('%d is prime:%s'%(number,prime))if__name__=='__main__':main()
fromconcurrentimportfuturesimporturllib.requestURLS=['http://www.foxnews.com/','http://www.cnn.com/','http://europe.wsj.com/','http://www.bbc.co.uk/','http://some-made-up-domain.com/']defload_url(url,timeout):returnurllib.request.urlopen(url,timeout=timeout).read()defmain():withfutures.ThreadPoolExecutor(max_workers=5)asexecutor:future_to_url=dict((executor.submit(load_url,url,60),url)forurlinURLS)forfutureinfutures.as_completed(future_to_url):url=future_to_url[future]try:print('%r page is%d bytes'%(url,len(future.result())))exceptExceptionase:print('%r generated an exception:%s'%(url,e))if__name__=='__main__':main()
The proposed design of this module was heavily influenced by theJava java.util.concurrent package[1]. The conceptual basis of themodule, as in Java, is the Future class, which represents the progressand result of an asynchronous computation. The Future class makeslittle commitment to the evaluation mode being used e.g. it can beused to represent lazy or eager evaluation, for evaluation usingthreads, processes or remote procedure call.
Futures are created by concrete implementations of the Executor class(called ExecutorService in Java). The reference implementationprovides classes that use either a process or a thread pool to eagerlyevaluate computations.
Futures have already been seen in Python as part of a popular Pythoncookbook recipe[2] and have discussed on the Python-3000 mailinglist[3].
The proposed design is explicit, i.e. it requires that clients beaware that they are consuming Futures. It would be possible to designa module that would return proxy objects (in the style ofweakref)that could be used transparently. It is possible to build a proxyimplementation on top of the proposed explicit mechanism.
The proposed design does not introduce any changes to Python languagesyntax or semantics. Special syntax could be introduced[4] to markfunction and method calls as asynchronous. A proxy result would bereturned while the operation is eagerly evaluated asynchronously, andexecution would only block if the proxy object were used before theoperation completed.
Anh Hai Trinh proposed a simpler but more limited API concept[5] andthe API has been discussed in some detail on stdlib-sig[6].
The proposed design was discussed on the Python-Dev mailing list[7].Following those discussions, the following changes were made:
Executor class was made into an abstract base classFuture.remove_done_callback method was removed due to a lackof convincing use casesFuture.add_done_callback method was modified to allow thesame callable to be added many timesFuture class’s mutation methods were better documented toindicate that they are private to theExecutor that created themThe reference implementation[8] contains a complete implementationof the proposed design. It has been tested on Linux and Mac OS X.
java.util.concurrent package documentationhttp://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/package-summary.htmlPython-3000 thread, “mechanism for handling asynchronous concurrency”https://mail.python.org/pipermail/python-3000/2006-April/000960.htmlPython3000 thread, “Futures in Python 3000 (was Re: mechanism for handling asynchronous concurrency)”https://mail.python.org/pipermail/python-3000/2006-April/000970.htmlstream, a similar concept proposed by Anh Hai Trinhhttp://www.mail-archive.com/stdlib-sig@python.org/msg00480.htmlfutures implementationhttp://code.google.com/p/pythonfutures/source/browse/#svn/branches/feedbackThis document has been placed in the public domain.
Source:https://github.com/python/peps/blob/main/peps/pep-3148.rst
Last modified:2025-02-01 08:59:27 GMT