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.