【问题标题】:Tornado coroutineTornado 协程
【发布时间】:2014-11-20 15:18:34
【问题描述】:

我正在尝试学习 tornado 协程,但使用以下代码时出错。

Traceback (most recent call last):
  File "D:\projekty\tornado\env\lib\site-packages\tornado\web.py", line 1334, in _execute
    result = yield result
  File "D:\projekty\tornado\env\lib\site-packages\tornado\gen.py", line 628, in run
    value = future.result()
  File "D:\projekty\tornado\env\lib\site-packages\tornado\concurrent.py", line 109, in result
    raise_exc_info(self._exc_info)
  File "D:\projekty\tornado\env\lib\site-packages\tornado\gen.py", line 631, in run
    yielded = self.gen.throw(*sys.exc_info())
  File "index.py", line 20, in get
    x = yield 'test'
  File "D:\projekty\tornado\env\lib\site-packages\tornado\gen.py", line 628, in run
    value = future.result()
  File "D:\projekty\tornado\env\lib\site-packages\tornado\concurrent.py", line 111, in result
    raise self._exception
BadYieldError: yielded unknown object 'test'

代码:

from tornado.ioloop import IOLoop
from tornado.web import RequestHandler, Application, url
from tornado import gen

class HelloHandler(RequestHandler):
    @gen.coroutine
    def get(self):
        x = yield 'test'
        self.render('hello.html')


def make_app():
    return Application(
        [url(r"/", HelloHandler)], 
        debug = True
    )

def main():
    app = make_app()
    app.listen(8888)
    IOLoop.instance().start()

main()

【问题讨论】:

  • 你想在x = yield 'test'线上做什么?
  • 这只是一个例子,我也尝试过产生一个函数的结果 - 结果是一样的,这一行只是为了测试协程

标签: python tornado


【解决方案1】:

正如 Lutz Horn 所指出的,tornado.coroutine 装饰器要求您只生成 Future 对象或包含 Future 对象的某些容器。所以试图产生str 会引发错误。我认为您缺少的部分是协程内您要调用yield something()something 的任何地方都必须也是协程,或者返回Future。例如,您可以像这样修复您的示例:

from tornado.gen import Return

class HelloHandler(RequestHandler):
    @gen.coroutine
    def get(self):
        x = yield self.do_test()
        self.render('hello.html')

    @gen.coroutine
    def do_test(self):
        raise Return('test')
        # return 'test' # Python 3.3+

甚至这个(虽然通常你不应该这样做):

class HelloHandler(RequestHandler):
    @gen.coroutine
    def get(self):
        x = yield self.do_test()
        self.render('hello.html')

    def do_test(self):
        fut = Future()
        fut.set_result("test")
        return fut

当然,这些都是人为的例子;因为我们实际上并没有在do_test 中做任何异步操作,所以没有理由让它成为协程。通常你会在那里做某种异步 I/O。例如:

class HelloHandler(RequestHandler):
    @gen.coroutine
    def get(self):
        x = yield self.do_test()
        self.render('hello.html')

    @gen.coroutine
    def do_test(self):
        http_client = AsyncHTTPClient()
        out = yield http_client.fetch("someurl.com") # fetch is a coroutine
        raise Return(out.body)
        # return out.body # Python 3.3+

【讨论】:

  • 感谢您的出色回答,我在函数 do_test 的开头添加了 time.sleep,一切都像同步代码一样工作,我在 sleep(x) 完成后执行 self.render,我认为也许这个功能是在后台执行的,但事实并非如此......那么使用协程有什么好处呢?也许另一个用户可以在前一个用户的睡眠完成之前同时打开连接?
  • @user3575996 使用yield function() 将使协程的执行等待function() 完成。但是,它不会阻塞 tornado I/O 循环,因此如果来自客户端的其他请求进来,则可以同时处理这些请求。但是,您不能使用time.sleep() 来测试它,因为它是一个阻塞函数。您需要使用非阻塞版本的睡眠。有关示例,请参见 this answer
  • 谢谢我终于明白了一切:)
  • 你能把raise Return ... 的例子改成 Python 3.3+ 吗?该指南说,可以在最新的 Python 版本中使用 returntornadoweb.org/en/stable/guide/… 似乎使 raise something 的做事方式过时了。
  • @Zelphir Python 2.7 仍然需要使用raise Return(...),这仍然是一个相当重要的用户群。我将更新示例以反映两种方法。
【解决方案2】:

From the documentation:

Tornado 中的大多数异步函数都返回 Future;产生这个对象会返回它的结果。

也可以yield a list or dict of Futures,同时启动,并行运行;完成后将返回结果列表或字典:

字符串"test" 不是Future。尝试产生一个。

【讨论】:

  • 假设我想产生数据库查询的结果,假设函数 get_all_users() 为我做这件事,你能给我一个例子,因为龙卷风示例使用了一些内置类,如 AsyncHTTPClient() ,如何实现自己的
猜你喜欢
  • 1970-01-01
  • 2018-11-30
  • 2020-10-16
  • 2015-07-02
  • 1970-01-01
  • 1970-01-01
  • 2018-11-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多