运行 Tornado 和另一个 asyncio long-运行 任务

Run Tornado alongside another asyncio long-running task

我想在 Python 3.7 的 asyncio 中 运行 一个 Tornado 服务器和一个独立的长期 运行ning 任务。我是 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())
  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 event loop,所以只需要启动一个,不需要同时启动两个。所以只需删除 start_tornado 中的 IOLoop.start() 调用即可。

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

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

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

  1. 您可以使用 tornado.ioloop.IOLoop.current().add_callback
  2. 而不是 await asyncio.create_task
  3. 您可以使用 yield gen.sleep(1)
  4. 而不是 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()