gpt4 book ai didi

python - *为什么* run_until_complete 不可重入。如何在没有线程的情况下逐步移植到异步?

转载 作者:行者123 更新时间:2023-12-04 15:35:57 26 4
gpt4 key购买 nike

假设我的任务是将 Flask 项目迁移到异步 Python 网络服务器。我正在寻找模式以尽量减少这里的工作量。在我看来,或多或少,不可能将同步网络服务器逐步移植到异步网络服务器中。这让我觉得我误解了异步。

假设我想使用 asyncio sql 库,并在 asyncio 网络服务器中使用它,我们可能必须将以下方法堆栈更改为异步:

if __name__=='__main__':
asyncio.get_event_loop().run_until_complete(main)

> async def main()
> async def subfunc()
> async def subsubfunc()
> async def decorator1()
> async def decorator2()
> async def webapi()
> async def decorator3()
> async def decorator4()
> async def servicemethod()
> async def servicemethod_impl()
....
> async def decorator5()
> async def decorator6()
> async def repositorylayer()
> async def sqllibrary()
> async def sqllibrary2()
> async def asyncio_socket.read()

^^ 因为我们要等待 asyncio_socket.read(),所以堆栈中的每个函数都需要更改 async def 函数声明以及 await关于它的依赖性。这对重构有一些严重的后果:

  • 我们最多需要更改 n 个函数才能获得一个 asyncio_socket.read() 的好处,其中大部分很少关心套接字读取是同步还是异步。那就是我们必须声明每个依赖函数异步并等待依赖的结果(!)
  • 过去依赖于此堆栈中的任何函数(但不在此堆栈中)的任何函数也必须更改为异步。 (!) 事件单元测试,我们今天可能对切换到异步不感兴趣,但必须更改:
result = oldtest()
assert result==expected
result = asyncio.get_event_loop().run_until_complete(oldtest())
assert result==expected

通常,任何调用异步函数的同步函数都需要重构 async-await —— 即异步具有传染性。任何调用异步的代码都必须感染异步,无论它是否关心异步。

因为这意味着全局重构,所以除了最小的项目外,将 web 服务从同步领域逐步移植到异步领域似乎并不实际。我见过将执行转移到同步/异步屏障处的线程的解决方案。然而,这似乎是: - 引入线程安全问题 - 删除异步的好处必须是通信和上下文切换 - 由于 GIL 而降低了执行吞吐量。

但是,原则上,应该可以从同步函数调用异步函数:

def syncfunc2():
result = asyncio.get_event_loop().run_until_complete(asyncfunc1())
return result

async def asyncfunc3():
result = await asyncfunc2()
return result

def syncfunc4():
result = asyncio.get_event_loop().run_until_complete(asyncfunc3())
return result

但是,由于不明确的原因,Python 不允许这样做并失败:

RuntimeError: This event loop is already running

我认为可以安全地实现可重入事件循环。我们过去常常在线程用完时为线程执行程序执行此操作——run_until_complete 的调用者可以驱动事件循环的执行直到它返回,之后执行返回到原始执行程序(这可以防止 no-more-executors-but -等待执行死锁)。这在 Python 中特别容易,因为 GIL 允许我们简单地保证 event_loop 是:

  • 不被其他功能驱动
  • 正在等待当前函数调用await

因此从队列中提取任务并执行它是安全的。因为如果你重新进入 run_until_complete Python 会报错,它禁止这个,也禁止增量引入 async。

所以:

  • 为什么 run_until_complete 不可重入?
  • 是否有可能在大型代码库中逐步引入异步而不求助于额外的线程(以及相应的异步优势损失)。
  • async 是否有效地将 python 代码库 fork 到那些使用 async 和不使用 async 的库中?

相关:

最佳答案

你想做什么 is to use gevent 。它允许您同时提供多个响应,而无需线程或对同步代码进行重大修改。

如果你想使用基于 asyncio 的方法,你将不得不面对这样一个事实,即每个处理网络 I/O 的函数(比如每个 Controller 或数据库交互)should be 都被重写了成为异步。这样做是为了帮助解决与并发相关的问题。这是始终确定功能可能突然屈服的地方的唯一方法。

关于python - *为什么* run_until_complete 不可重入。如何在没有线程的情况下逐步移植到异步?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59738489/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com