在__del __

问题描述

我想定义本质上是关闭资源的异步__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进行了建议,上面已经测试过。使用更新的asyncio.create_task的Python文档suggests,但在非异步情况下,它会直接引发错误,因为没有当前循环。我最后的绝望尝试是使用asyncio.run,它适用于非异步情况,但不适用于异步情况,因为在已经有一个线程的线程中调用runprohibited。运行循环。此外,文档指出,应该在程序中只能被调用一次。

我还是异步事物的新手。如何实现?


用例一词,因为在注释中提到异步上下文管理器是首选方法。我同意,将它们用于短期资源管理将是理想的。但是,由于两个原因,我的用例有所不同。

  • 该类的用户不一定知道基础资源。最好对不喜欢资源本身的用户隐藏隐藏资源的资源。
  • 需要在同步上下文中实例化该类(或使其可以实例化),并且通常只创建一次。例如,在Web服务器上下文中,将在全局范围内实例化该类,然后在端点定义中使用其异步功能

例如:

asc = Async()

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

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

解决方法

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

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


带有代码段的完整示例:

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()