Credit: David Perry
You want to run a time-consuming function in a separate thread while allowing the main thread to continue uninterrupted.
The Future
class sometimes allows you to hide the
fact that you’re using threading while still taking
advantage of threading’s potential performance
advantages:
from threading import * import copy class Future: def _ _init_ _(self, func, *param): # constructor self._ _done = 0 self._ _result = None self._ _status = 'working' self._ _C = Condition( ) # Notify on this Condition when result is ready # Run the actual function in a separate thread self._ _T = Thread(target=self.Wrapper, args=(func, param)) self._ _T.setName("FutureThread") self._ _T.start( ) def _ _repr_ _(self): return '<Future at '+hex(id(self))+':'+self._ _status+'>' def _ _call_ _(self): self._ _C.acquire( ) while self._ _done==0: self._ _C.wait( ) self._ _C.release( ) # Deepcopy _ _result to prevent accidental tampering with it result = copy.deepcopy(self._ _result) return result def isDone(self): return self._ _done def Wrapper(self, func, param): # Run the actual function and housekeep around it self._ _C.acquire( ) self._ _result = func(*param) self._ _done=1 self._ _status=`self._ _result` self._ _C.notify( ) self._ _C.release( )
Although Python’s thread syntax is nicer than the
syntax in many languages, it can still be a pain if all you want to
do is run a time-consuming function in a separate thread while
allowing the main thread to continue uninterrupted. A
Future
object provides a legible and intuitive way
to achieve such an end.
To run a function in a separate thread, simply put it in a
Future
object:
>>> A=Future(longRunningFunction, arg1, arg2 ...)
Both the calling thread and the execution of the function will
continue on their merry ways until the caller needs the
function’s result. When it does, the caller can read
the result by calling Future
like a function. For
example:
>>> print A( )
If the Future
object has completed executing, the
call returns immediately. If it is still running, the call (and the
calling thread in it) blocks until the function completes. The result
of the function is stored in an attribute of the
Future
instance, so subsequent calls to it return
immediately.
Since you wouldn’t expect to be able to change the
result of a function, Future
objects are not meant
to be mutable. This is enforced by requiring
Future
to be called, rather than directly reading
_ _result
. If desired, stronger enforcement of
this rule can be achieved by playing with _ _getattr_ _
and _ _setattr_ _
or, in Python 2.2,
by using property
.
Future
runs its function only once, no matter how
many times you read it. Thus, you will have to recreate
Future
if you want to rerun your function (e.g.,
if the function is sensitive to the time of day).
For example, suppose you have a function named
muchComputation
that can take a rather long time
(tens of seconds or more) to compute its results, because it churns
along in your CPU or it must read data from the network or from a
slow database. You are writing a GUI, and a button on that GUI needs
to start a call to muchComputation
with suitable
arguments, displaying the results somewhere on the GUI when done. You
can’t afford to run the function itself as the
command
associated with the button, since if you
did, the whole GUI would appear to freeze until the computation is
finished, and that is unacceptable. Future
offers
one easy approach to handling this situation. First, you need to add
a list of pending Future
instances that are
initially empty to your application object called, for example,
app.futures
. When the button is clicked, execute
something like this:
app.futures.append(Future(muchComputation, with, its, args, here))
and then return, so the GUI keeps being serviced
(Future
is now running the function, but in
another thread). Finally, in some periodically executed poll in your
main thread, do something like this:
for future in app.futures[:]: # Copy list and alter it in loop if future.isDone( ): appropriately_display_result(future( )) app.futures.remove(future)