如何在异步循环关闭之前等待对象的__del__完成? [英] How can I wait for an object's __del__ to finish before the async loop closes?

查看:186
本文介绍了如何在异步循环关闭之前等待对象的__del__完成?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个将在其中包含aiohttp.ClientSession对象的类。

I have a class that will have an aiohttp.ClientSession object in it.

通常在使用时

async with aiohttp.ClientSession() as session:  
   # some code

由于调用了会话的__aexit__方法,因此该会话将关闭。

The session will close since the session's __aexit__ method is called.

我无法使用上下文管理器,因为我想保持会话对于

I cant use a context manager since I want to keep the session persistent for the entire lifetime of the object.

这有效:

import asyncio
import aiohttp

class MyAPI:
    def __init__(self):
        self.session = aiohttp.ClientSession()

    def __del__(self):
        # Close connection when this object is destroyed
        print('In __del__ now')
        asyncio.shield(self.session.__aexit__(None, None, None))



async def main():
    api = MyAPI()

asyncio.run(main())

但是在某些地方n引发异常,在__aexit__方法完成之前关闭事件循环。
我该如何克服?

However if in some place an exception is raised, the event loop is closed before the __aexit__ method is finished. How can I overcome this?

stacktrace:

stacktrace:

Traceback (most recent call last):
  File "/home/ron/.PyCharm2018.3/config/scratches/async.py", line 19, in <module>
    asyncio.run(main())
  File "/usr/local/lib/python3.7/asyncio/runners.py", line 43, in run
    return loop.run_until_complete(main)
  File "/usr/local/lib/python3.7/asyncio/base_events.py", line 568, in run_until_complete
    return future.result()
  File "/home/ron/.PyCharm2018.3/config/scratches/async.py", line 17, in main
    raise ValueError
ValueError
In __del__ now
Exception ignored in: <function MyAPI.__del__ at 0x7f49982c0e18>
Traceback (most recent call last):
  File "/home/ron/.PyCharm2018.3/config/scratches/async.py", line 11, in __del__
  File "/usr/local/lib/python3.7/asyncio/tasks.py", line 765, in shield
  File "/usr/local/lib/python3.7/asyncio/tasks.py", line 576, in ensure_future
  File "/usr/local/lib/python3.7/asyncio/events.py", line 644, in get_event_loop
RuntimeError: There is no current event loop in thread 'MainThread'.
sys:1: RuntimeWarning: coroutine 'ClientSession.__aexit__' was never awaited
Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0x7f49982c2e10>


推荐答案

不要使用 __del __ 挂钩来清理异步资源。您根本不能指望它被调用,更不用说控制何时使用它,或者那时异步循环是否仍然可用。您真的想明确地处理此问题。

Don't use a __del__ hook to clean up asynchronous resources. You can't count it being called at all, let alone control when it'll be used or if the async loop is still available at that time. You really want to handle this explicitly.

要么使API成为异步上下文管理器,要么在退出时显式清理资源,最后使用 处理程序,例如; with async with 语句基本上旨在封装传统上通常由处理的资源清理块。

Either make the API an async context manager, or otherwise explicitly clean up resources at exit, with a finally handler, say; the with and async with statements are basically designed to encapsulate resource cleanup traditionally handled in finally blocks.

我将 API 实例设为上下文管理器:

I'd make the API instance a context manager here:

class MyAPI:
    def __init__(self):
        self.session = aiohttp.ClientSession()

    async def __aenter__(self):
        return self

    async def __aexit__(self, *excinfo):
        await self.session.close()

请注意,所有 ClientSession .__ aexit __()真正的作用是在 self.close()上等待,因此以上内容直接适用于该协程。

Note that all that ClientSession.__aexit__() really does is await on self.close(), so the above goes straight to that coroutine.

然后使用

async def main():
    async with MyAPI() as api:
        pass

另一种选择是将自己的会话对象提供给 MyAPI 实例和自己负责在完成后将其关闭:

Another option is to supply your own session object to the MyAPI instance and take responsibility yourself for closing it when you are done:

class MyAPI:
    def __init__(self, session):
        self.session = session

async def main():
    session = aiohttp.ClientSession()
    try:
        api = MyAPI(session)
        # do things with the API
    finally:
        await session.close()

这篇关于如何在异步循环关闭之前等待对象的__del__完成?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆