différés torsadés / rappels et l'exécution asynchrone
-
13-10-2019 - |
Question
Je suis en train de comprendre comment puis-je faire mon code plus asynchrone en utilisant torsadé.
- Une fonction retourne un objet différé
- puis-je ajouter une liste des callbacks
- le premier rappel sera appelée après la fonction différée fournit un résultat par
deferred_obj.callback
- puis, dans la chaîne de callbacks, le premier rappel fera quelque chose avec les données et appelez le deuxième rappel
- et etc.
callbacks cependant enchaînées ne sera pas considérée comme asynchrone parce qu'ils sont enchaînées et la boucle d'événement continuer à tirer chacun d'eux en même temps jusqu'à ce qu'il n'y a pas de plus, non?
Cependant, si j'ai un objet différé, et je joins en tant que rappel le deferred_obj.callback comme dans d.addCallback(deferred_obj.callback)
alors ce sera considéré comme asynchrone, car le deferred_obj attend les données, puis la méthode qui transmettre les données est en attente de données aussi bien, mais une fois que je d.callback « d » objet traite les données puis appelez deferred_obj.callback cependant, puisque cet objet est reporté, contrairement au cas des callbacks enchaînées, il exécute de manière asynchrone ... correcte?
En supposant que tout mon code est non-bloquant, cela signifie que callbacks chaînées ne sont PAS asynchrones tandis que deferreds sont enchaînées, correct?
La solution
Les rappels sont (par défaut) synchrone. Cependant, comme points de Twisted doc sur:
Si vous avez besoin d'un Reporté à attendre sur une autre, tout ce que vous devez faire est de retour différée aux d'une méthode ajouté à addCallbacks.
Vous pouvez l'utiliser pour faire un peu de traitement asynchrone dans votre chaîne de rappel. Faisons que:
from twisted.internet import reactor, defer
def callback_func_2(result, previous_data):
# here we pass the result of the deferred down the callback chain
# (done synchronously)
print "calling function 1 on result:%s with previous result:%s" % (result, previous_data)
return result
def callback_func(result):
#let's do some asynchronous stuff in this callback
# simple trick here is to return a deferred from a callback
# instead of the result itself.
#
# so we can do asynchronous stuff here,
# like firing something 1 second later and have
# another method processing the result
print "calling function 1 on result:%s" % result
d = defer.Deferred()
reactor.callLater(1, d.callback, "second callback")
d.addCallback(callback_func_2, result)
return d
def do():
d = defer.Deferred()
reactor.callLater(1, d.callback, "first callback")
d.addCallback(callback_func)
return d
do()
reactor.run()
Autres conseils
Un peu, mais il n'y a pas dans ce type concurrency de traitement des événements. Pas de nouveau rappel sera appelé jusqu'à ce que le code revient à la boucle d'événements. Ainsi, la chaîne de callbacks est synchrone. Il est seulement asynchrone dans la boucle d'événements.
Ceci est une mise en garde de ce type de programmation, les gestionnaires les plus exécutent rapidement et revenir à la boucle d'événements le plus tôt possible. Il ne devrait pas faire une tâche fastidieuse dans un gestionnaire.
L'utilisation d'un différé ne rend pas votre asynchrone de code.
import time
from twisted.internet import defer
from twisted.internet import reactor
def blocking(duration, deferred):
print "start blocking"
time.sleep(duration)
print "finished blocking"
deferred.callback(True)
def other_task():
print "working..."
reactor.callLater(1, other_task)
def finish(result):
print "stopping reactor in 2sec"
reactor.callLater(2, reactor.stop)
def failed(reason):
print reason
print "stopping reactor in 2sec"
reactor.callLater(2, reactor.stop)
def main():
d = defer.Deferred()
d.addCallbacks(finish, failed)
reactor.callLater(0, blocking, 5, d)
if __name__ == "__main__":
reactor.callLater(0, other_task)
main()
reactor.run()
Si vous avez de longue date code synchrone, vous pouvez deferToThread ou diviser en itérations courtes en utilisant un coopérateur (twisted.internet.task)
Si vous voulez rendre votre code plus asynchrone avec une approche propre, puis vérifier ce cadre:
https://github.com/iogf/untwisted
Il a un code propre avec une documentation claire. La façon de traiter les modèles asynchrones est simple.