Easy-to-use framework which makes threading almost as simple as normal function calling.
BackgroundCall is a high-level (threading.Thread replacement) concept for non-blocking execution of time consuming functions - yet it focuses naturally on result-returning/conscious termination in a functional style (without imposing an extra Java-style alter ego class layout). It also handles exception (transfer) issues.
See also the twin brother recipe "CallQueue".
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | def example_BackgroundCall():
import urllib,time
def work():
return urllib.urlopen('http://www.python.org/').read()
bkcall=BackgroundCall(work)
print 'work() executing in background ...'
while not bkcall.is_done():
print '.',
time.sleep(0.010)
print 'done.'
print bkcall.get_return()[:500]
import sys
from time import time as _time, sleep as _sleep
class Full(Exception):pass
class Empty(Exception):pass
class BackgroundCall:
"""BackgroundCall
Example:
bkcall=BackgroundCall( time_consuming_function )
...
if bkcall.is_done():
print "got", bkcall.get_return()
"""
id=None
done=0 #1=returned; 2=exception raised
def __init__(self, func, args=(), kwargs={}):
import thread
def thread_bkcall():
try:
self.ret=func(*args, **kwargs)
self.done=1
except:
self.exc=sys.exc_info()
self.done=2
self.id=thread.start_new(thread_bkcall, ())
def is_done(self):
return self.done
def get_return(self, wait=1, timeout=None, raise_exception=1, alt_return=None):
"""delivers the return value or (by default) echoes the exception of
the call job
wait: 0=no waiting; Attribute error raised if no
1=waits for return value or exception
callable -> waits and wait()-call's while waiting for return
"""
if not self.done and wait:
starttime=_time()
delay=0.0005
while not self.done:
if timeout:
remaining = starttime + timeout - _time()
if remaining <= 0: #time is over
if raise_exception:
raise Empty, "return timed out"
else:
return alt_return
delay = min(delay * 2, remaining, .05)
else:
delay = min(delay * 2, .05)
if callable(wait): wait()
_sleep(delay) #reduce CPU usage by using a sleep
if self.done==2: #we had an exception
exc=self.exc
del self.exc
if raise_exception & 1: #by default exception is raised
raise exc[0],exc[1],exc[2]
else:
return alt_return
return self.ret
def get_exception(self):
return self.exc
if __name__=='__main__':
example_BackgroundCall()
|
BackgroundCall is the twin brother of recipe "CallQueue".
The motto is: 'The programmer controls the program - not reverse'
Unlike the Java-ish stiff threading.Thread with .run()/.start()/.isAlive()/.join() and Queue.Queue with .put()/.get()/.task_done()/.join(), BackgroundCall and CallQueue are maybe a more natural high-level "threading" API for the dynamic-typing, functional language Python.
In fact one could almost forget about the term "thread", but have "functions" all the way: Threads/deamons return None/never ?
Have requested that kind of layout for addition to threading / Queue. Feedback / Suggestions?