2010-07-26 8 views
1

C'est ce que j'essaie d'accomplir. Je fais un appel à distance à un serveur pour l'information, et je veux bloquer pour attendre l'information. J'ai créé une fonction qui renvoie un différé tel que lorsque le RPC arrive avec la réponse, le différé est appelé. Ensuite, j'ai une fonction appelée à partir d'un fil qui va threads.blockingCallFromThread(reactor, deferredfunc, args).tordu: vérifier si un différé a déjà été appelé

Si quelque chose se passe mal - par exemple, le serveur tombe en panne - alors l'appel ne sera jamais débloqué. Je préférerais que le différé s'en aille avec une exception dans ces cas-là.

J'ai partiellement réussi. J'ai un différé, onConnectionLost qui s'éteint lorsque la connexion est perdue. J'ai modifié ma fonction d'appel de blocage à:

deferred = deferredfunc(args) 
    self.onConnectionLost.addCallback(lambda _: deferred.errback(
     failure.Failure(Exception("connection lost while getting run")))) 
    result = threads.blockingCallFromThread(
     reactor, lambda _: deferred, None) 
    return result 

Cela fonctionne très bien. Si le serveur tombe en panne, la connexion est perdue et le retour est déclenché. Cependant, si le serveur ne s'arrête pas et que tout se ferme correctement, onConnectionLost est toujours déclenché et le rappel anonyme tente ici de déclencher la erreur, provoquant une exception AlreadyCalled.

Existe-t-il un moyen simple de vérifier qu'un différé a déjà été déclenché? Je veux éviter de l'emballer dans un bloc try/except, mais je peux toujours recourir à cela si c'est le seul moyen.

Répondre

4

Il y a des moyens, mais vous ne devriez vraiment pas le faire. Votre code qui déclenche le Deferred devrait garder une trace de savoir s'il a tiré le Deferred ou pas dans l'état associé. Vraiment, lorsque vous lancez le Deferred, vous devriez perdre la trace de celui-ci afin qu'il puisse être récupéré correctement. De cette façon, vous n'aurez jamais à vous soucier de l'appeler deux fois, puisque vous n'en aurez plus aucune référence.

En outre, il semble que vous appelez deferredfunc à partir du même thread que vous appelez blockingCallFromThread. Ne fais pas ça; Les fonctions qui renvoient Deferreds appellent très probablement des API de réacteurs, et ces API sont non thread-safe. En fait, Deferred lui-même n'est pas thread-safe. C'est pourquoi c'est blockingCallFromThread, pas blockOnThisDeferredFromThread. Vous devriez faire blockingCallFromThread(reactor, deferredfunc, args).

Si vous voulez vraiment le comportement errback-if-it-called-else-do-nothing, vous pouvez vouloir cancel différé.

+0

alors comment annuler le différé si je n'y ai pas accès, puisque je dois appeler 'blockingCallFromThread' pour être thread-safe? est le seul moyen de modifier cette fonction pour faire 'onConnectionLost.addCallback (lambda _: d.cancel())', où 'd' est le différé sur le point de revenir? cela signifierait que vous deviez passer l'onconnectionlost différé – Claudiu

+1

Vous pouvez appeler 'blockingCallFromThread (self.something)', où 'self.something' reste sur' Deferred' pour vous. Le problème est d'appeler des API de réacteurs à partir du mauvais thread, pas du mauvais objet. – Glyph

+0

Oui, j'ai fini par faire ça. Je ne pouvais pas obtenir .cancel à travailler, cependant - il semblait n'avoir aucun effet. Je viens de faire une fonction d'aide qui prend un différé et essaie de déclencher un errback. Y a-t-il des nuances à '.cancel' que je ne connais pas? à partir des docs, il semblait que si je ne faisais rien de spécial, '.cancel' devrait déclencher un errback de CancelledError s'il n'a pas été appelé, et ne rien faire autrement. – Claudiu

Questions connexes