Skip to content

Instantly share code, notes, and snippets.

@jomido
Last active September 28, 2016 13:56
Show Gist options
  • Save jomido/8ff072d5d1406f3c277dc78654457f9b to your computer and use it in GitHub Desktop.
Save jomido/8ff072d5d1406f3c277dc78654457f9b to your computer and use it in GitHub Desktop.
Twisted Sync & Async (@in_thread & @in_reactor)
import random
import time
from twisted.internet import task
from twisted.internet.defer import (
inlineCallbacks as inline,
returnValue,
DeferredList as when_all
)
from utils import in_thread, in_reactor, sleep, repeating
@in_thread
def cpu_burner(t):
print ("cpu burner ({}) starts burning".format(t))
time.sleep(t)
result = random.randint(1, 10)
print ('cpu burner ({}) is done burning, result = {}'.format(t, result))
print ('cpu burner ({}) is calling the network'.format(t))
n = some_network_call(result)
print ("cpu burner ({}) is done the network call".format(t))
return n
@in_reactor
@inline
def some_network_call(x):
yield sleep(1.5)
result = x * 2
returnValue(result)
@repeating(1.0)
def tick():
print("tick")
@inline
def main(reactor):
tick()
# SEQUENTIAL CALLS
result = yield cpu_burner(5)
print("result: {}".format(result))
result = yield cpu_burner(4)
print("result: {}".format(result))
# CONCURRENT CALLS
results = yield when_all([cpu_burner(t) for t in [3, 7, 9]])
for result in results:
success, payload = result
print ('{}: {}'.format(success, payload))
if __name__ == "__main__":
task.react(main)
tick
cpu burner (5) starts burning
tick
tick
tick
tick
tick
tick
cpu burner (5) is done burning, result = 9
cpu burner (5) is calling the network
tick
tick
cpu burner (5) is done the network call
result: 18
cpu burner (4) starts burning
tick
tick
tick
tick
tick
cpu burner (4) is done burning, result = 9
cpu burner (4) is calling the network
tick
tick
cpu burner (4) is done the network call
result: 18
cpu burner (3) starts burning
cpu burner (7) starts burning
cpu burner (9) starts burning
tick
tick
tick
tick
cpu burner (3) is done burning, result = 8
cpu burner (3) is calling the network
tick
tick
cpu burner (3) is done the network call
tick
tick
tick
tick
cpu burner (7) is done burning, result = 6
cpu burner (7) is calling the network
tick
tick
cpu burner (7) is done the network call
cpu burner (9) is done burning, result = 9
cpu burner (9) is calling the network
tick
tick
cpu burner (9) is done the network call
True: 16
True: 12
True: 18
from functools import wraps
from twisted.internet import reactor, threads
from twisted.internet import defer
def in_reactor(fn):
"""
Decorate a function to make it always run in the main reactor loop.
"""
@wraps(fn)
def wrapper(*args, **kwargs):
return threads.blockingCallFromThread(reactor, fn, *args, **kwargs)
return wrapper
def in_thread(fn):
"""
Decorate a function to make it always run in a thread.
"""
@wraps(fn)
def wrapper(*args, **kwargs):
return threads.deferToThread(fn, *args, **kwargs)
return wrapper
def sleep(secs):
"""
sleep for :secs seconds
"""
d = defer.Deferred()
reactor.callLater(secs, d.callback, None)
return d
def repeating(period):
"""
Decorate a function/method to make it repeat every :period. If the callable
returns False, cancel the repetition.
NOTE: you still have to do an initial call on the function/method to kick
it off
"""
def wrapper(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
if fn(*args, **kwargs) is not False:
reactor.callLater(period, wrapper, *args, **kwargs)
return wrapper
return wrapper
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment