2012-01-14 5 views
2

J'espère obtenir des éclaircissements sur la meilleure façon de traiter le "premier" différé, c'est-à-dire non seulement ajouter des rappels et des erreurs aux méthodes Twisted existantes qui renvoient un différé, mais la meilleure façon de créer ces originaux différés.Python Twisted différé: clarification nécessaire

Comme exemple concret, voici 2 variantes de la même méthode: il compte seulement le nombre de lignes dans certains fichiers texte assez grand, et est utilisé comme point de départ pour une chaîne de deferreds.

Procédé 1: Celui-ci ne se sent pas très bien, car le différé est déclenché directement par la méthode reactor.callLater.

def get_line_count(self): 
    deferred = defer.Deferred() 

    def count_lines(result): 
     try: 
      print_file = file(self.print_file_path, "r") 
      self.line_count = sum(1 for line in print_file) 
      print_file.close() 
      return self.line_count 
     except Exception as inst: 
      raise InvalidFile() 

    deferred.addCallback(count_lines) 
    reactor.callLater(1, deferred.callback, None) 
    return deferred 

Méthode 2: légèrement mieux, que le report est en fait tiré lorsque le résultat est disponible

def get_line_count(self): 
    deferred = defer.Deferred() 

    def count_lines(): 
     try: 
      print_file = file(self.print_file_path, "r") 
      self.line_count = sum(1 for line in print_file) 
      print_file.close() 
      deferred.callback(self.line_count) 
     except Exception as inst: 
      deferred.errback(InvalidFile()) 

    reactor.callLater(1, count_lines) 
    return deferred 

Note: Vous pouvez également remarquer que ces deux sont méthodes réellement synchrones, et potentiellement bloquantes, (et je pourrais peut-être utiliser "MaybeDeferred"?). Mais bon, c'est en fait l'un des aspects qui me déroute.

  1. Pour Méthode 2, si la méthode count_lines est très lent (en comptant les lignes dans certains gros fichiers, etc.), sera potentiellement « bloquer » l'ensemble app Twisted? Je lis beaucoup de documentation sur la façon dont les callbacks et les erreurs et le réacteur se comportent ensemble (les callbacks doivent être exécutés rapidement, ou renvoyés différemment, etc.), mais dans ce cas, je ne vois pas et j'apprécierais vraiment/etc exemples

  2. y at-il des articles/ claires explications qui traitent de la meilleure approche à la création de ces « premiers » deferreds? J'ai lu these excellent articles, et ils ont beaucoup aidé avec une compréhension de base, mais je me sens toujours comme si je manquais un morceau.

  3. Pour blocage de code, serait-ce ce cas typicall pour DeferToThread ou reactor.spawnprocess? J'ai lu beaucoup de questions comme this one et this article, mais je ne suis toujours pas sûr à 100% sur la façon de traiter avec le code potentiellement bloquer, pour la plupart lorsqu'ils traitent avec le fichier i/o

Désolé si tout cela semble trop basique, mais je veux vraiment avoir l'habitude d'utiliser Twisted de manière plus approfondie. (Il a été un outil très puissant pour tous les aspects plus orientés réseau). Merci pour votre temps!

Répondre

2

Oui, vous avez raison: vous avez besoin de threads ou de processus séparés pour éviter de bloquer la boucle Twisted. L'utilisation de Deferreds ne fera pas magiquement que votre code ne soit pas bloquant.Pour vos questions:

  1. Oui, vous bloqueraient la boucle d'événement si count_lines est très lent. Le reporter à un fil résoudrait cela.

  2. J'ai utilisé Twisteds documentation pour apprendre comment fonctionnent différées, mais je suppose que vous avez déjà vécu cela. L'article sur database support était information car il dit clairement que cette bibliothèque est construite en utilisant des discussions. C'est ainsi que vous comblez l'écart synchrone-asynchrone. Si l'appel est vraiment bloquant, vous devez DeferToThread. Python lui-même est un peu à thread unique, ce qui signifie qu'un seul thread peut exécuter le code Python à la fois. Cependant, si le thread que vous créez bloquera de toute façon les E/S, ce modèle fonctionnera correctement: le thread libérera le verrou de l'interpréteur global et laissera ainsi les autres threads Python s'exécuter, y compris le thread principal avec la boucle Twisted.

    Vous pouvez également utiliser des E/S non bloquantes dans votre code. Cela peut être fait avec le module select, par exemple. Dans ce cas, vous n'avez pas besoin d'un thread séparé. Twisted utilise cette technique en interne et vous n'avez pas à y penser si vous faites des E/S réseau normales. Mais si vous faites quelque chose d'exotique, alors il est bon de savoir comment les choses sont construites pour que vous puissiez faire la même chose.

J'espère que cela rend les choses un peu plus claires!

+0

Merci beaucoup pour la réponse rapide et complète, Martin! 1. et 2. D'accord, l'approche Thread/Threadpool * a du sens dans ce cas. –

+0

3. J'ai tendance à me méfier des threads en Python à cause du GIL, mais normalement ** devrait ** être capable de gérer 3-4 threads utilisant les méthodes 'count_lines' à peu près en même temps sans gâcher l'événement Twisted loop (la méthode est utilisée dans de petites "tâches" qui envoient ensuite les données depuis les fichiers vers les périphériques via la série) –

+0

Merci pour l'info sur le module select, il semble être "lié" aux différents types de réacteurs dans Twisted. Je vais regarder dans plus de détails! Pour une utilisation pratique, malheureusement, il ne supporte que les sockets sur les plates-formes Windows (d'où les limitations plutôt étranges que j'avais lors de l'utilisation de support série torsadée sur Windows). –

Questions connexes