【问题标题】:Using an asynchronous function in __del__在 __del__ 中使用异步函数
【发布时间】:2020-08-20 17:56:36
【问题描述】:

我想定义本质上是关闭资源的异步__del__。这是一个例子。

import asyncio

class Async:
    async def close(self):
        print('closing')
        return self

    def __del__(self):
        print('destructing')
        asyncio.ensure_future(self.close())

async def amain():
    Async()

if __name__ == '__main__':
    asyncio.run(amain())

这样可以正常打印destructingclosing。但是,如果资源是在异步函数之外定义的,则会调用 __del__,但永远不会执行关闭。

def main():
    Async()

这里没有发出警告,但打印显示关闭没有完成。如果异步函数已运行,但在其外部创建了任何实例,则会发出警告

def main2():
    Async()
    asyncio.run(amain())

RuntimeWarning:从未等待协程“Async.close”

这一直是12 中的主题,但两者都没有我要找的东西,或者我不知道怎么看。特别是第一个问题是关于删除资源的,它的答案建议使用上面测试过的asyncio.ensure_future。 Python 文档suggests 使用较新的asyncio.create_task,但它在非异步情况下直接引发错误,没有当前循环。我最后的绝望尝试是使用asyncio.run,它适用于非异步情况,但不适用于异步情况,因为在已经有一个运行循环的线程中调用runprohibited。此外,文档指出它应该在程序中只被调用一次。

我对异步事物还是陌生的。这是如何实现的?


关于用例的一句话,因为在 cmets 中提到了异步上下文管理器作为首选替代方案。我同意,将它们用于短期资源管理将是理想的。但是,我的用例有所不同,原因有两个。

  • 类的用户不一定知道底层资源。对不喜欢资源本身的用户隐藏关闭资源会带来更好的用户体验。
  • 需要在同步上下文中实例化该类(或使其能够实例化),并且通常只创建一次。例如,在 Web 服务器上下文中,该类将在全局范围内实例化,之后其异步函数将在端点定义中使用。

例如:

asc = Async()

server.route('/', 'GET')
async def root():
    return await asc.do_something(), 200

我对实现此类功能的其他建议持开放态度,但在这一点上,即使是我对这样做的可能性的好奇心也足以让我想要回答这个特定问题,而不仅仅是一般问题。

【问题讨论】:

  • __del__ 不适合资源管理。您想使用上下文管理器来确保某些对象在完成后关闭。
  • @chepner 感谢您的关注。但是例如在这种情况下,如果类将在端点之外(即在全局范围内)定义并在所有端点中使用,那么异步服务器将相当不方便。
  • 我不认为你想要的可以实现,特别是如果你想支持所有的用例。从同步代码触发异步解除分配尤其是一个问题,其中不仅事件循环没有运行,而且最终将运行的事件循环将是由asyncio.run 新创建的。除了通过像猴子补丁asyncio.run 这样的严重黑客攻击外,我不知道如何处理这个问题。 chepner 正确地指出异步上下文管理器是这里的方法。我不确定我是否理解你反对他们的理由;你能详细说明一下吗?
  • @user4815162342 我在问题中详细说明了我的用例!

标签: python asynchronous async-await python-asyncio


【解决方案1】:

唯一想到的是在服务器关闭后运行清理。它看起来像这样:

asc = Async()

try:
  asyncio.run(run_server())  # You already do it now somewhere
finally:
  asyncio.run(asc.close())

由于asyncio.run 每次都会创建新的事件循环,您可能希望更深入地重用相同的事件循环:

loop = asyncio.get_event_loop()
asc = Async()

try:
  loop.run_until_complete(run_server())
finally:
  loop.run_until_complete(asc.close())

只要您知道自己在做什么,就可以多次致电run_until_complete


您的 sn-p 的完整示例:

import asyncio


class Async:
    async def close(self):
        print('closing')
        return self

    async def cleanup(self):
        print('destructing')
        await self.close()


loop = asyncio.get_event_loop()
asc = Async()


async def amain():
    await asyncio.sleep(1)  # Do something


if __name__ == '__main__':
    try:
        loop.run_until_complete(amain())
    finally:
        loop.run_until_complete(asc.cleanup())
        loop.close()

【讨论】:

  • 这很公平,尽管这不是我要求的。这可能是最好的方法。非常感谢!
猜你喜欢
  • 1970-01-01
  • 2022-11-10
  • 1970-01-01
  • 2019-01-20
  • 2020-08-13
  • 1970-01-01
  • 1970-01-01
  • 2020-03-09
  • 2017-04-08
相关资源
最近更新 更多