在Tornado异步处理程序中使用简单的python生成器作为协同例程?
我有一个python生成器函数,可以生成文本块。我想为迭代生成器的在Tornado异步处理程序中使用简单的python生成器作为协同例程?,python,asynchronous,web,generator,tornado,Python,Asynchronous,Web,Generator,Tornado,我有一个python生成器函数,可以生成文本块。我想为迭代生成器的tornado.web.RequestHandler子类编写一个get方法,在响应进行时将块写入响应 因为这是Tornado,而且生成器可能需要一秒钟的时间来处理,所以我认为最好使处理程序异步,将此生成器用作一个协例程,并在每个块之后将控制传递给IOLoop。然而,我不知道该怎么做 下面是我的示例(阻塞)代码: 如何使此处理程序异步工作?以下是您所描述内容的基本版本。为了避免阻塞,可以通过回调函数将生成器传递给IOLoop。这里的
tornado.web.RequestHandler
子类编写一个get
方法,在响应进行时将块写入响应
因为这是Tornado,而且生成器可能需要一秒钟的时间来处理,所以我认为最好使处理程序异步,将此生成器用作一个协例程,并在每个块之后将控制传递给IOLoop。然而,我不知道该怎么做
下面是我的示例(阻塞)代码:
如何使此处理程序异步工作?以下是您所描述内容的基本版本。为了避免阻塞,可以通过回调函数将生成器传递给IOLoop。这里的诀窍在于,您没有使用执行实际IO的进程,因此没有操作系统级的进程/文件处理程序可通过
add\u handler
添加到IOLoop中,您可以改为使用一个简单的add_callback
调用,并从回调函数中重复调用它,以将函数保留在IOLoop回调队列中,直到生成器完成为止
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()
还可以使用新接口来异步进程:
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()
现在还不清楚你要实现什么目标。是否要在迭代所有生成器值之前离开get(),然后在新值准备就绪时返回?如果是这样,你就不能这样做。在这个特定的函数中,您的代码是单线程的,如果退出,则会丢失上下文。另一方面,标记为asynchronous(通常意味着处理程序)的方法是从线程池中调用的,因此,可以在那里进行阻塞。只要生成器存在,它就具有我需要的所有上下文。这就是生成器的美妙之处:在一个线程中执行联合例程。当然,你必须自己处理日程安排,这可能是这里真正的问题。是的,这看起来正是我想要的。我没有想到将循环计划本身作为回调。@PhilofiniteJest次要评论,最好使用IOLoop.current()而不是IOLoop.instance()。就我而言,这是一个残酷的过程。我想我知道那里发生了什么,但控制流程更为神秘(没有深入了解gen.Task在幕后的工作)@cptphil对定时回调的使用更为直接。此外,如果使用的生成器生成空字符串,则最好使用
if response not None
而不是if response
。示例不会,但我的实际用例会:)关于任务
的任何进一步文档?我正在努力从阅读资料中了解全局。
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()