Professional Documents
Culture Documents
>>> Python Developer's Guide (/dev/) >>> PEP Index (/dev/peps/) >>> PEP 3148 - futures - execute computations asynchronously
3148
Title:
Author:
Status:
Final
Type:
Standards Track
Created:
16-Oct-2009
Python-Version:
3.2
Post-History:
Contents
Abstract (#abstract)
Motivation (#motivation)
Specification (#specification)
Naming (#naming)
Interface (#interface)
Executor (#executor)
ProcessPoolExecutor (#processpoolexecutor)
ThreadPoolExecutor (#threadpoolexecutor)
Future Objects (#future-objects)
Internal Future Methods (#internal-future-methods)
Module Functions (#module-functions)
Check Prime Example (#check-prime-example)
Web Crawl Example (#web-crawl-example)
Rationale (#rationale)
Reference Implementation (#reference-implementation)
References (#references)
Copyright (#copyright)
Abstract (#id17)
This PEP proposes a design for a package that facilitates the evaluation of callables using threads and processes.
Motivation (#id18)
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.
Specification (#id19)
Naming (#id20)
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". 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.
Interface (#id21)
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.
Executor (#id22)
Executor is an abstract class that provides methods to execute calls asynchronously.
submit(fn,*args,**kwargs)
Schedulesthecallabletobeexecutedasfn(*args,**kwargs)andreturnsaFutureinstance
representingtheexecutionofthecallable.
ThisisanabstractmethodandmustbeimplementedbyExecutorsubclasses.
map(func,*iterables,timeout=None)
Equivalenttomap(func,*iterables)butfuncisexecutedasynchronouslyandseveralcallsto
funcmaybemadeconcurrently.ThereturnediteratorraisesaTimeoutErrorif__next__()is
calledandtheresultisn'tavailableaftertimeoutsecondsfromtheoriginalcalltomap().Iftimeout
isnotspecifiedorNonethenthereisnolimittothewaittime.Ifacallraisesanexceptionthenthat
exceptionwillberaisedwhenitsvalueisretrievedfromtheiterator.
shutdown(wait=True)
Signaltheexecutorthatitshouldfreeanyresourcesthatitisusingwhenthecurrentlypending
futuresaredoneexecuting.CallstoExecutor.submitandExecutor.mapandmadeaftershutdown
willraiseRuntimeError.
IfwaitisTruethenthismethodwillnotreturnuntilallthependingfuturesaredoneexecutingand
theresourcesassociatedwiththeexecutorhavebeenfreed.IfwaitisFalsethenthismethodwill
returnimmediatelyandtheresourcesassociatedwiththeexecutorwillbefreedwhenallpending
futuresaredoneexecuting.Regardlessofthevalueofwait,theentirePythonprogramwillnotexit
untilallpendingfuturesaredoneexecuting.
__enter__()
__exit__(exc_type,exc_val,exc_tb)
Whenusinganexecutorasacontextmanager,__exit__willcall
Executor.shutdown(wait=True).
ProcessPoolExecutor (#id23)
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 pickleable according to the same limitations as the
multiprocessing module.
Calling Executor or Future methods from within a callable submitted to a ProcessPoolExecutor will result in deadlock.
__init__(max_workers)
Executescallsasynchronouslyusingapoolofamostmax_workersprocesses.Ifmax_workersis
Noneornotgiventhenasmanyworkerprocesseswillbecreatedasthemachinehasprocessors.
ThreadPoolExecutor (#id24)
The ThreadPoolExecutor class is an Executor subclass that uses a pool of threads to execute calls asynchronously.
Deadlock can occur when the callable associated with a Future waits on the results of another Future . For example:
importtime
defwait_on_b():
time.sleep(5)
print(b.result())#bwillnevercompletebecauseitiswaitingona.
return5
defwait_on_a():
time.sleep(5)
print(a.result())#awillnevercompletebecauseitiswaitingonb.
return6
executor=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)
#Thiswillnevercompletebecausethereisonlyoneworkerthreadand
#itisexecutingthisfunction.
print(f.result())
executor=ThreadPoolExecutor(max_workers=1)
executor.submit(wait_on_future)
__init__(max_workers)
Executescallsasynchronouslyusingapoolofatmostmax_workersthreads.
Attempttocancelthecall.Ifthecalliscurrentlybeingexecutedthenitcannotbecancelledandthe
methodwillreturnFalse,otherwisethecallwillbecancelledandthemethodwillreturnTrue.
cancelled()
ReturnTrueifthecallwassuccessfullycancelled.
running()
ReturnTrueifthecalliscurrentlybeingexecutedandcannotbecancelled.
done()
ReturnTrueifthecallwassuccessfullycancelledorfinishedrunning.
result(timeout=None)
Returnthevaluereturnedbythecall.Ifthecallhasn'tyetcompletedthenthismethodwillwaitup
totimeoutseconds.Ifthecallhasn'tcompletedintimeoutsecondsthenaTimeoutErrorwillbe
raised.IftimeoutisnotspecifiedorNonethenthereisnolimittothewaittime.
IfthefutureiscancelledbeforecompletingthenCancelledErrorwillberaised.
Ifthecallraisedthenthismethodwillraisethesameexception.
exception(timeout=None)
Returntheexceptionraisedbythecall.Ifthecallhasn'tyetcompletedthenthismethodwillwaitup
totimeoutseconds.Ifthecallhasn'tcompletedintimeoutsecondsthenaTimeoutErrorwillbe
raised.IftimeoutisnotspecifiedorNonethenthereisnolimittothewaittime.
IfthefutureiscancelledbeforecompletingthenCancelledErrorwillberaised.
IfthecallcompletedwithoutraisingthenNoneisreturned.
add_done_callback(fn)
Attachesacallablefntothefuturethatwillbecalledwhenthefutureiscancelledorfinishes
running.fnwillbecalledwiththefutureasitsonlyargument.
Addedcallablesarecalledintheorderthattheywereaddedandarealwayscalledinathread
belongingtotheprocessthataddedthem.IfthecallableraisesanExceptionthenitwillbelogged
andignored.IfthecallableraisesanotherBaseExceptionthenbehaviorisnotdefined.
Ifthefuturehasalreadycompletedorbeencancelledthenfnwillbecalledimmediately.
ShouldbecalledbyExecutorimplementationsbeforeexecutingtheworkassociatedwiththe
Future.
IfthemethodreturnsFalsethentheFuturewascancelled,i.e.Future.cancelwascalledand
returnedTrue.AnythreadswaitingontheFuturecompleting(i.e.throughas_completed()or
wait())willbewokenup.
IfthemethodreturnsTruethentheFuturewasnotcancelledandhasbeenputintherunning
state,i.e.callstoFuture.running()willreturnTrue.
ThismethodcanonlybecalledonceandcannotbecalledafterFuture.set_result()or
Future.set_exception()havebeencalled.
set_result(result)
SetstheresultoftheworkassociatedwiththeFuture.
set_exception(exception)
SetstheresultoftheworkassociatedwiththeFuturetothegivenException.
WaitfortheFutureinstances(possiblycreatedbydifferentExecutorinstances)givenbyfsto
complete.Returnsanamed2tupleofsets.Thefirstset,named"done",containsthefuturesthat
completed(finishedorwerecancelled)beforethewaitcompleted.Thesecondset,named
"not_done",containsuncompletedfutures.
timeoutcanbeusedtocontrolthemaximumnumberofsecondstowaitbeforereturning.Iftimeout
isnotspecifiedorNonethenthereisnolimittothewaittime.
return_whenindicateswhenthemethodshouldreturn.Itmustbeoneofthefollowingconstants:
Constant
Description
FIRST_COMPLETED
Themethodwillreturnwhenanyfuturefinishesoris
cancelled.
FIRST_EXCEPTION
Themethodwillreturnwhenanyfuturefinishesbyraising
anexception.Ifnotfutureraisesanexceptionthenitis
equivalenttoALL_COMPLETED.
ALL_COMPLETED
as_completed(fs,timeout=None)
Themethodwillreturnwhenallcallsfinish.
ReturnsaniteratorovertheFutureinstancesgivenbyfsthatyieldsfuturesastheycomplete
(finishedorwerecancelled).Anyfuturesthatcompletedbeforeas_completed()wascalledwillbe
yieldedfirst.ThereturnediteratorraisesaTimeoutErrorif__next__()iscalledandtheresult
isn'tavailableaftertimeoutsecondsfromtheoriginalcalltoas_completed().Iftimeoutisnot
specifiedorNonethenthereisnolimittothewaittime.
TheFutureinstancescanhavebeencreatedbydifferentExecutorinstances.
fromconcurrentimportfutures
importmath
PRIMES=[
112272535095293,
112582705942171,
112272535095293,
115280095190773,
115797848077099,
1099726899285419]
defis_prime(n):
ifn%2==0:
returnFalse
sqrt_n=int(math.floor(math.sqrt(n)))
foriinrange(3,sqrt_n+1,2):
ifn%i==0:
returnFalse
returnTrue
defmain():
withfutures.ProcessPoolExecutor()asexecutor:
fornumber,primeinzip(PRIMES,executor.map(is_prime,
PRIMES)):
print('%disprime:%s'%(number,prime))
if__name__=='__main__':
main()
fromconcurrentimportfutures
importurllib.request
URLS=['http://www.foxnews.com/',
'http://www.cnn.com/',
'http://europe.wsj.com/',
'http://www.bbc.co.uk/',
'http://somemadeupdomain.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('%rpageis%dbytes'%(
url,len(future.result())))
exceptExceptionase:
print('%rgeneratedanexception:%s'%(
url,e))
if__name__=='__main__':
main()
Rationale (#id30)
The proposed design of this module was heavily influenced by the the Java java.util.concurrent package [1] (#id9) . 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.
Futures are created by concrete implementations of the Executor class (called ExecutorService in Java). The reference
implementation provides classes that use either a process or a thread pool to eagerly evaluate computations.
Futures have already been seen in Python as part of a popular Python cookbook recipe [2] (#id10) and have discussed on the Python3000 mailing list [3] (#id11) .
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.
The proposed design does not introduce any changes to Python language syntax or semantics. Special syntax could be introduced
[4] (#id12) 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.
Anh Hai Trinh proposed a simpler but more limited API concept [5] (#id13) and the API has been discussed in some detail on stdlib-sig
[6] (#id14) .
The proposed design was discussed on the Python-Dev mailing list [7] (#id15) . 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
References (#id32)
[1]
(#id1)
summary.html (http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/package-summary.html)
[2]
(#id2)
(http://code.activestate.com/recipes/84317/)
[3]
(#id3)
3000/2006-April/000960.html (http://mail.python.org/pipermail/python-3000/2006-April/000960.html)
[4]
Python 3000 thread, "Futures in Python 3000 (was Re: mechanism for handling asynchronous concurrency)"
(#id4)
http://mail.python.org/pipermail/python-3000/2006-April/000970.html (http://mail.python.org/pipermail/python-3000/2006April/000970.html)
[5]
(#id5)
sig@python.org/msg00480.html (http://www.mail-archive.com/stdlib-sig@python.org/msg00480.html)
[6]
(#id6)
(http://mail.python.org/pipermail/stdlib-sig/2009-November/000731.html)
[7]
(#id7)
(http://mail.python.org/pipermail/python-dev/2010-March/098169.html)
[8]
(#id8)
(http://code.google.com/p/pythonfutures/source/browse/#svn/branches/feedback)
Copyright (#id33)
This document has been placed in the public domain.
Source: https://hg.python.org/peps/file/tip/pep-3148.txt (https://hg.python.org/peps/file/tip/pep-3148.txt)
Tweets by @ThePSF
The PSF
The Python Software Foundation is the organization behind Python. Become a member of the PSF and help advance the
software and our mission.
Back to Top
Back to Top