如何在异步协程中包装同步函数?

新手上路,请多包涵

我正在使用 aiohttp 构建一个 API 服务器,该服务器将 TCP 请求发送到单独的服务器。发送 TCP 请求的模块是同步的,对我来说是一个黑盒子。所以我的问题是这些请求阻塞了整个 API。我需要一种方法将模块请求包装在一个不会阻塞其余 API 的异步协程中。

因此,仅使用 sleep 作为一个简单的示例,有没有办法以某种方式将耗时的同步代码包装在非阻塞协程中,如下所示:

 async def sleep_async(delay):
    # After calling sleep, loop should be released until sleep is done
    yield sleep(delay)
    return 'I slept asynchronously'

原文由 Zac Delventhal 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.4k
2 个回答

最终我在 这个线程 中找到了答案。我正在寻找的方法是 run_in_executor 。这允许同步函数异步运行而不会阻塞事件循环。

在我上面发布的 sleep 示例中,它可能看起来像这样:

 import asyncio
from time import sleep

async def sleep_async(loop, delay):
    # None uses the default executor (ThreadPoolExecutor)
    await loop.run_in_executor(None, sleep, delay)
    return 'I slept asynchronously'

另请参阅以下答案 -> 我们如何调用需要协程的普通函数?

原文由 Zac Delventhal 发布,翻译遵循 CC BY-SA 4.0 许可协议

您可以使用装饰器将同步版本包装到异步版本。

 import time
from functools import wraps, partial

def wrap(func):
    @wraps(func)
    async def run(*args, loop=None, executor=None, **kwargs):
        if loop is None:
            loop = asyncio.get_event_loop()
        pfunc = partial(func, *args, **kwargs)
        return await loop.run_in_executor(executor, pfunc)
    return run

@wrap
def sleep_async(delay):
    time.sleep(delay)
    return 'I slept asynchronously'

过时了,aioify是维护模式

或者使用 aioify

% pip install aioify

然后

@aioify
def sleep_async(delay):
    pass

原文由 ospider 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题