【问题标题】:asyncio - how many coroutines?asyncio - 有多少协程?
【发布时间】:2018-04-04 20:45:21
【问题描述】:

我已经在 python 应用程序中苦苦挣扎了几天,我希望在文件夹中查找一个或多个文件,并遍历每个文件和其中的每个记录,并创建要在 Janusgraph 上持久保存的对象数据库。我正在使用的特定 OGM 要求使用 asyncio 以异步方式完成与数据库的事务。我已经阅读了很多关于 asyncio 的博客和帖子,并且我想我了解 async、await、task 等的概念......在我的应用程序中,我定义了几个处理不同部分的函数:

  • 检索所有可用文件的列表
  • 选择一个文件进行处理
  • 遍历选定的文件并读取一行/记录进行处理
  • 接收记录,确定解析 from in 并调用其他几个函数,这些函数负责在模型对象被持久化到数据库之前创建模型对象。例如,我创建了不同的函数:用户、会话、浏览器、设备使用、服务器等......

我理解(我可能是错的)使用 asyncio 的一大优势是在对函数的调用通常会因 I/O、数据库事务、网络延迟等而阻塞的情况下......

所以我的问题是我是否需要将所有我的函数转换为协程并安排在事件循环中运行,或者只是那些会阻塞的函数,例如将事务提交到数据库。我一开始就尝试了这种方法,但遇到了各种各样的问题。

【问题讨论】:

  • 如果你有一个现有的同步代码库,你通常可以通过将所有阻塞调用的函数转换为异步,然后跟随调用者链直到顶部,以便每个函数至少有时直接或间接调用异步的东西现在是异步的。但是,如果您是从头开始工作,通常最好在编写任何内容之前考虑这些异步链。

标签: python python-asyncio janusgraph goblin


【解决方案1】:

所以我的问题是我是否需要将所有我的函数转换为协程并安排运行事件循环,或者只是那些会阻塞的函数,

您可能需要转换其中的大部分,但转换应该主要是机械的,归结为将def 更改为async def,并在调用其他协程时添加await

显然,您无法避免转换实际阻塞的那些,要么切换到适当的 asyncio API,要么使用loop.run_in_executor() 来处理那些没有的。 (DNS 解析曾经是后者的一个突出例子。)

但是你还需要转换它们的调用者,因为从阻塞函数调用协程是没有用的,除非该函数实现了类似事件循环的功能。另一方面,当从另一个协程调用协程时,一切正常,因为挂起会自动传播到链的顶部。一旦整个调用链由协程组成,顶层的协程就会使用loop.create_task()loop.run_until_complete() 馈送到事件循环中。

当然,既不阻塞也不调用阻塞函数的便利函数可以安全地保持非异步,并且由同步或异步代码调用,没有任何区别。


以上适用于实现无堆栈协程的 asyncio。 greenlet 使用了一种不同的方法,其任务封装了调用堆栈,这允许它们在使用普通函数调用的代码中的任意位置进行切换。不过,Greenlets 比协程更重量级且更不便携,所以我首先转换为 asyncio。

【讨论】:

  • 你回答的几个后续问题:(1)我有一个主协程,它将位于我读取记录的链的顶部,我假设需要喂这个协程到事件循环。该函数将依次调用其他正在执行内存数据操作或创建对象的函数。如果我正确理解了您的陈述,我不需要使用 await 调用后续的?可以将它们称为正常功能吗? (2) 我还假设我也可以使用 asyncio.ensure_future() 来喂顶级协程,对吧?
  • @Cracoras (1) 确切地说,如果您正在调用执行内存中内容的简单函数,则不需要将它们转换为协程,因此不需要等待 - 它是可以将它们称为正常功能。 (2) 是;给定一个协程,asyncio.ensure_futureloop.create_task 是等价的,但当参数已知是协程时,后者是 the intended API。您可以将create_task(和ensure_future)视为“在后台”启动任务。另请查看asyncio.gather
  • 我终于完成了代码的重构,把所有的内部函数都改回了普通函数。但是我收到以下错误:file_stats, db_session = process_function(message_types.index(row[0]), row, file_stats, db_session) TypeError: 'coroutine' object is not iterable 这行是我最后一个协程里面在我的堆栈中调用并触发我提到的所有剩余的内存绑定函数。
  • @Cracoras 你需要await 协程。如果它位于调用链的最顶端,则需要使用loop.run_until_complete 调用它。
  • 您好 @user4815162342 在您上次发表评论后,我再次回到代码并意识到您所说的 process_function 调用协程是什么意思。事实证明,这些都不应该是协程,而是常规函数。我将它们还原回来并且能够运行代码而没有任何问题/异常。我仍然会做一些额外的测试,但它似乎正在工作。非常感谢您的所有帮助,当然也感谢您的耐心。我希望我不必再为这个问题打扰你了 ;-) --MD
猜你喜欢
  • 1970-01-01
  • 2020-12-29
  • 1970-01-01
  • 2016-09-21
  • 2020-10-16
  • 2018-02-25
  • 2019-03-16
  • 1970-01-01
  • 2018-12-09
相关资源
最近更新 更多