Welcome, guest | Sign In | My Account | Store | Cart

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".

Python, 78 lines
 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?