2012-01-11 29 views
16

Tôi có chức năng trình tạo python để tạo ra khối văn bản. Tôi muốn viết một phương thức get cho một lớp con tornado.web.RequestHandler sẽ lặp qua máy phát, viết các khối ra để phản hồi khi nó đi.Sử dụng trình tạo python đơn giản làm đồng bộ trong trình xử lý async Tornado?

Vì đây là Tornado và vì máy phát có thể mất hơn một giây để xử lý, tôi nghĩ sẽ tốt hơn nếu làm cho trình xử lý không đồng bộ, sử dụng trình tạo này làm đồng thời và chuyển điều khiển sang IOLoop sau mỗi chunk. Tuy nhiên, tôi không thể làm cho người đứng đầu hoặc đuôi của làm thế nào để làm điều này.

Dưới đây là ví dụ của tôi (chặn) mã:

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 

Làm thế nào tôi có thể làm cho công việc xử lý này không đồng bộ?

+0

Nó không thực sự rõ ràng những gì bạn sẽ đạt được. Bạn có muốn thoát khỏi get() trước khi tất cả các giá trị máy phát điện được lặp lại và quay lại khi các giá trị mới đã sẵn sàng chưa? Nếu vậy, hơn bạn không thể làm điều đó. Trong hàm cụ thể này, mã của bạn là một luồng đơn và nếu bạn thoát thì bối cảnh lỏng lẻo. Mặt khác, phương thức được đánh dấu là không đồng bộ mà thường ngụ ý trình xử lý được gọi là tạo thành một nhóm luồng, do đó, nó sẽ là OK để chặn ở đó. – real4x

+0

Miễn là máy phát điện tồn tại, nó có tất cả ngữ cảnh tôi cần. Đó là vẻ đẹp của máy phát điện: đồng-thói quen trong một chủ đề duy nhất. Tất nhiên, bạn phải tự xử lý lịch trình, có lẽ đây là vấn đề thực sự ở đây. –

Trả lời

16

Đây là phiên bản cơ bản của những gì bạn mô tả. Để tránh bị chặn, bạn có thể truyền máy phát của mình tới IOLoop thông qua chức năng gọi lại. Bí quyết ở đây là vì bạn không sử dụng quy trình thực hiện IO và do đó không có trình xử lý tệp/xử lý cấp os để thêm vào IOLoop qua add_handler, bạn có thể sử dụng cuộc gọi add_callback đơn giản và gọi nó lặp đi lặp lại từ bên trong chức năng gọi lại để giữ chức năng trong hàng đợi gọi lại IOLoop cho đến khi máy phát điện kết thúc.

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

Tại sao có, trông giống như những gì tôi muốn. Tôi đã không nghĩ rằng có lịch trình vòng lặp chính nó như là một cuộc gọi lại. –

+1

@philofinfinitejest Nhận xét nhỏ, tốt hơn là sử dụng IOLoop.current() thay vì IOLoop.instance(). Trong trường hợp của tôi đã được crusial. Nó cũng được đề xuất bởi [docs] (http://tornado.readthedocs.org/en/latest/ioloop.html?highlight=ioloop#tornado.ioloop.IOLoop.current) – prokher

14

Nó cũng có thể sử dụng giao diện mới tornado's gen quy trình async:

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

Tôi nghĩ rằng tôi thấy những gì đang xảy ra ở đó, nhưng dòng chảy của kiểm soát là bí ẩn hơn (w/o có một sự hiểu biết sâu sắc về những gì gen.Task không đằng sau hậu trường). Việc sử dụng các callbacks được lập lịch của @ cptphil dễ dàng hơn nhiều. –

+0

Ngoài ra, có thể tốt hơn nếu sử dụng 'nếu đáp ứng không phải là None' thay vì' if response', trong trường hợp chúng ta đang sử dụng một trình tạo ra chuỗi rỗng. Ví dụ sẽ không, nhưng trường hợp sử dụng thực tế của tôi sẽ. :) –

+1

+1 không nhận thức được cơn lốc xoáy – philofinfinitejest

Các vấn đề liên quan