【问题标题】:Run Tornado alongside another asyncio long-running task与另一个异步长时间运行的任务一起运行 Tornado
【发布时间】:2020-03-01 01:42:50
【问题描述】:

我想在 Python 3.7 中的 asyncio 中运行 Tornado 服务器以及独立的长时间运行的任务。我是asyncio 的新手。我读过你应该只调用一次asyncio.run(),所以我将这两个任务放在main() 方法下,这样我就可以将一个参数传递给asyncio.run()。当我运行此代码时,我收到错误 TypeError: a coroutine was expected, got <function start_tornado at 0x105c8e6a8>。我想让代码在没有错误的情况下运行,但最终我想知道如何以正确的方式做到这一点。我在下面编写的代码感觉就像是一个丑陋的 hack。

import asyncio
import datetime
import tornado.ioloop
import tornado.web


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")

def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
    ])

# Fake background task I got from here:
# https://docs.python.org/3/library/asyncio-task.html#sleeping
async def display_date():
    while True:
        print(datetime.datetime.now())
        await asyncio.sleep(1)

async def start_tornado():
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

async def main():
    await asyncio.create_task(start_tornado)
    await asyncio.create_task(display_date)

asyncio.run(main())

【问题讨论】:

    标签: python tornado python-asyncio


    【解决方案1】:
    1. 当您将create_taskasync def 函数一起使用时,正常调用该函数,然后将结果传递给create_task

      await asyncio.create_task(start_tornado())
      await asyncio.create_task(display_date())
      
    2. 如果您要立即使用await,则无需使用create_task。使用不带awaitcreate_task 在后台启动任务,例如display_date()start_tornado 在这个意义上不是后台任务,因为它没有无限循环,它只是启动一个由 Tornado 放入后台的服务器。所以我会这样写:

      await start_tornado()
      asyncio.create_task(display_date())
      
    3. 从 Tornado 5.0 开始,Tornado IOLoop 和 asyncio 事件循环默认是集成的,所以你只需要启动一个,不需要同时启动。所以只需删除start_tornado 中的IOLoop.start() 调用。

    4. start_tornado 目前没有做任何异步操作,所以它可能只是一个普通函数。但它也是一个合理的地方,可以添加异步启动逻辑,例如建立数据库连接,因此您可以将其保留为协程。

    代码的工作版本与我的编辑:https://repl.it/@bdarnell/FarawayAdmiredConversions

    【讨论】:

    • 错误消失了,但是当我运行你的程序版本时,它在一个循环后退出。知道为什么吗?我期待display_date 中的无限循环让程序无限期地运行。
    • 没关系,看来我只是需要在 asyncio.create_task(display_date()) 前面加上一个 await
    【解决方案2】:

    Tornado 可能已经拥有你需要的一切。

    1. 您可以使用tornado.ioloop.IOLoop.current().add_callback,而不是使用await asyncio.create_task
    2. 你可以用yield gen.sleep(1)代替await asyncio.sleep(1)

    因此,您示例中的整个代码最终可能如下所示:

    import datetime
    import tornado.ioloop
    import tornado.web
    import tornado.gen as gen
    
    
    class MainHandler(tornado.web.RequestHandler):
        def get(self):
            self.write("Hello, world")
    
    
    def make_app():
        return tornado.web.Application([
            (r"/", MainHandler),
        ])
    
    
    @gen.coroutine
    def display_date():
        while True:
            print(datetime.datetime.now())
            yield gen.sleep(1)
    
    
    def start_tornado():
        app = make_app()
        app.listen(8888)
        tornado.ioloop.IOLoop.current().add_callback(display_date)
        tornado.ioloop.IOLoop.current().start()
    
    
    start_tornado()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-06-28
      • 1970-01-01
      • 1970-01-01
      • 2014-03-31
      • 2016-05-23
      • 2014-06-21
      • 2022-01-03
      相关资源
      最近更新 更多