You can use a Future, which is not included in the standard library, but very simple to implement:
from threading import Thread, Event
class Future(object):
def __init__(self, thunk):
self._thunk = thunk
self._event = Event()
self._result = None
self._failed = None
Thread(target=self._run).start()
def _run(self):
try:
self._result = self._thunk()
except Exception, e:
self._failed = True
self._result = e
else:
self._failed = False
self._event.set()
def wait(self):
self._event.wait()
if self._failed:
raise self._result
else:
return self._result
You would use this particular implementation like this:
import time
def work():
for x in range(3):
time.sleep(1)
print 'Tick...'
print 'Done!'
return 'Result!'
def main():
print 'Starting up...'
f = Future(work)
print 'Doing more main thread work...'
time.sleep(1.5)
print 'Now waiting...'
print 'Got result: %s' % f.wait()
Unfortunately, when using a system that has no "main" thread, it's hard to tell when to call "wait"; you obviously don't want to stop processing until you absolutely need an answer.
With Twisted, you can use deferToThread
, which allows you to return to the main loop. The idiomatically equivalent code in Twisted would be something like this:
import time
from twisted.internet import reactor
from twisted.internet.task import deferLater
from twisted.internet.threads import deferToThread
from twisted.internet.defer import inlineCallbacks
def work():
for x in range(3):
time.sleep(1)
print 'Tick...'
print 'Done!'
return 'Result!'
@inlineCallbacks
def main():
print 'Starting up...'
d = deferToThread(work)
print 'Doing more main thread work...'
yield deferLater(reactor, 1.5, lambda : None)
print "Now 'waiting'..."
print 'Got result: %s' % (yield d)
although in order to actually start up the reactor and exit when it's finished, you'd need to do this as well:
reactor.callWhenRunning(
lambda : main().addCallback(lambda _: reactor.stop()))
reactor.run()
The main difference with Twisted is that if more "stuff" happens in the main thread - other timed events fire, other network connections get traffic, buttons get clicked in a GUI - that work will happen seamlessly, because the deferLater
and the yield d
don't actually stop the whole thread, they only pause the "main" inlineCallbacks
coroutine.