2012-01-11 17 views
16

Metin parçalarını veren bir python üreteci işlevim var. Ben gider yanıta parçalarını yerinden yazma, jeneratör yineleme olacak tornado.web.RequestHandler alt sınıf için get yöntemi yazmak istiyorum.Tornado async işleyicide basit bir python jeneratörü bir rutin olarak mı kullanılıyor?

Bu Tornado olduğundan ve jeneratörü işlemek için bir saniyeyi alabildiğinden beri, bu jeneratörü bir eş-rutin olarak kullanarak ve IOLoop'a her kontrolden sonra kontrolünü geçerek asenkronize etmek iyi olacağını düşündüm. yığın. Ancak, bunun nasıl yapılacağına dair kafa ya da kuyruk yapamam.

class TextHandler(web.RequestHandler): 
    @web.asynchronous 
    def get(self, n): 
     generator = self.generate_text(100000) 
     # Clearly, this will block. How to make it asynchronous? 
     for text in generator: 
      self.write(text) 

    def generate_text(n): 
     for x in xrange(n): 
      if not x % 15: 
       yield "FizzBuzz\n" 
      elif not x % 5: 
       yield "Buzz\n" 
      elif not x % 3: 
       yield "Fizz\n" 
      else: 
       yield "%s\n" % x 

nasıl uyumsuz bu işleyici iş yapabilirsiniz:

İşte benim örnek (engelleme) kodu?

+0

Hedefleriniz için neler olduğunu gerçekten net değil. Tüm jeneratör değerleri tekrarlanmadan önce get() öğesini bırakmak ve yeni değerler hazır olduğunda geri dönmek ister misiniz? Eğer öyleyse, bunu yapamazsın. Bu özel işlevde kodunuz tek bir iş parçacığıdır ve siz çıktıktan sonra bağlamı kaybedersiniz. Öte yandan, genellikle işleyiciyi ima eden asenkron olarak işaretlenen yönteme bir iplik havuzu denir, bu nedenle orada engellemek için Tamam olmalıdır. – real4x

+0

Jeneratör mevcut olduğu sürece, ihtiyacım olan tüm içeriğe sahiptir. Bu jeneratörler güzelliği: Tek bir iş parçacığında rutinleri. Tabii ki, programlamayı kendiniz halletmelisiniz, ki bu belki de asıl sorun. –

cevap

16

Burada açıklayan şeyin temel bir versiyonu. Engellemeyi önlemek için, jeneratörünüzü IOLoop'a bir geri çağırma işleviyle iletebilirsiniz. İşin püf noktası burada gerçek IO yapar ve böylece add_handler aracılığıyla IOLoop eklemek için hiçbir os seviyesi süreci/dosya işleyicisi olan bir işlem kullanılarak olmadığı için, bunun yerine basit bir add_callback çağrısı kullanmak ve geri arama işlevi içinde tekrar tekrar çağırabilir olduğunu Jeneratör bittiğinde IOLoop geri arama kuyruğunda işlevi korumak için.

import tornado.httpserver 
import tornado.ioloop 
import tornado.web 

class TextHandler(tornado.web.RequestHandler): 
    @tornado.web.asynchronous 
    def get(self): 
     self.generator = self.generate_text(1000) 
     tornado.ioloop.IOLoop.instance().add_callback(self.loop) 

    def loop(self): 
     try: 
      text = self.generator.next() 
      self.write(text) 
      tornado.ioloop.IOLoop.instance().add_callback(self.loop) 
     except StopIteration: 
      self.finish() 

    def generate_text(self, n): 
     for x in xrange(n): 
      if not x % 15: 
       yield "FizzBuzz\n" 
      elif not x % 5: 
       yield "Buzz\n" 
      elif not x % 3: 
       yield "Fizz\n" 
      else: 
       yield "%s\n" % x 

application = tornado.web.Application([ 
    (r"/text/", TextHandler), 
]) 

http_server = tornado.httpserver.HTTPServer(application) 
http_server.listen(8888) 
tornado.ioloop.IOLoop.instance().start() 
+0

Neden evet, tam olarak istediğim gibi görünüyor. Döngü takvimini kendi kendine geri çağırma olarak düşünmemiştim. –

+1

@philofinfinitejest küçük nokta, bunun yerine IOLoop.instance ait()) IOLoop.current (kullanımı daha iyidir. Benim durumumda bu haç oldu. Ayrıca [docs] tarafından tavsiye edilmektedir (http://tornado.readthedocs.org/en/latest/ioloop.html?highlight=ioloop#tornado.ioloop.IOLoop.current) ben orada neler olduğunu görüyorum – prokher

14

O zaman uyumsuz işlemlere yeni tornado's gen arayüzü kullanmak da mümkündür:

import tornado.httpserver 
import tornado.ioloop 
import tornado.web 
import tornado.gen 

class TextHandler(tornado.web.RequestHandler): 

    @tornado.web.asynchronous 
    @tornado.gen.engine 
    def get(self): 

     def cb(it, callback): 
      try: 
       value = it.next() 
      except StopIteration: 
       value = None 
      callback(value) 

     it = self.generate_text(1000) 
     while True: 
      response = yield tornado.gen.Task(cb, it) 
      if response: 
       self.write(response) 
      else: 
       break 
     self.finish() 

    def generate_text(self, n): 
     for x in xrange(n): 
      if not x % 15: 
       yield "FizzBuzz\n" 
      elif not x % 5: 
       yield "Buzz\n" 
      elif not x % 3: 
       yield "Fizz\n" 
      else: 
       yield "%s\n" % x 

application = tornado.web.Application([ 
    (r"/text/", TextHandler), 
]) 

http_server = tornado.httpserver.HTTPServer(application) 
http_server.listen(8888) 
tornado.ioloop.IOLoop.instance().start() 
+0

, ama kontrol akışı daha gizemli (ne kadar gen.Task'ın perde arkasına yaptığı konusunda derin bir anlayışa sahip değil). @ cptphil'in planlı geri aramaların kullanımı çok daha basittir. –

+0

Ayrıca, tepki yerine 'eğer response' ait None' edilmemişse, boş dizeleri veren bir jeneratör kullanıyorsanız durumda,' kullanmak daha iyi olabilir. Örnek olmayacak, ama benim gerçek kullanım durumum olacak. :) –

+1

+1 tornado.gen'in farkında değildi – philofinfinitejest