【问题标题】:calling sync functions from async function从异步函数调用同步函数
【发布时间】:2019-02-14 17:51:27
【问题描述】:

我正在尝试将flask 应用程序移植到quart 以利用asyncio。我认为我目前的方法行不通,因为我的整个函数链都是在没有考虑异步的情况下编写的 - 请考虑以下几点:

def long_running_task(task):
    result = some_synchronous_function(task)
    return result

@app.route('/<task>', methods=['GET'])
async def do_task(task):
    ok = await long_running_task(task)
    if ok:
        return (ok.result)
    else:
        return ('Something went wrong')

如果long_running_task 及其整个函数调用链未声明为async,我是否真的从我的路由声明为async 中获得任何好处?

【问题讨论】:

  • 不,那根本行不通。你只能await awaitables — 异步函数等。要使用异步的强大功能,你需要将你的 I/O(数据库访问等)转换为异步访问。

标签: python python-asyncio quart


【解决方案1】:

要从 asyncio 运行阻塞同步函数,而不阻塞主事件循环,您可以使用 loop.run_in_executor()ThreadPoolExecutorProcessPoolExecutor` 中运行阻塞函数(即在其自己的线程或进程中)。

从你想要调用它的异步函数中:

loop = asyncio.get_event_loop()

result = await loop.run_in_executor(None, long_running_task, task)

第一个参数None 是告诉它使用循环的默认执行器。显然do_task() 仍然需要等待result 完成,但在等待期间,其他异步任务将能够在事件循环中运行。

【讨论】:

  • 是否可以在同一个loop.run_in_executor调用多个函数同时运行?
  • @y_159 查看asyncio.gather(),您可以使用它来并行运行它们。
  • @LukeSapan asyncio.gather() 用于同时运行asyncio..create_task() 创建的任务。我将如何将其用于loop.run_in_executor()。我认为它开始执行阻塞功能而不创建任务对象?我的问题是如何使用单个loop.run_in_executor() 同时运行多个阻塞函数。
  • @y_159 你可以在asyncio.gather(...) 内多次调用loop.run_in_executor。即使您多次调用loop.run_in_executor,它们都将使用默认的共享执行程序,因此您不会每次都产生新的执行程序。或者,如果您不想使用默认执行程序,您可以创建自己的共享执行程序,并将其作为第一个参数传递给每个调用(而不是上面示例中显示的 None)。