Skip to main content
 首页 » 编程设计

python之如何在异步循环关闭之前等待对象的 __del__ 完成

2025年05月04日20youxin

我有一个包含 aiohttp.ClientSession 对象的类。

通常当你使用

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

session 将关闭,因为 session 的 __aexit__ 方法被调用。

我不能使用上下文管理器,因为我想在对象的整个生命周期内保持 session 持久。

这个有效:

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

然而,如果在某个地方引发异常,事件循环会在 __aexit__ 方法完成之前关闭。 我该如何克服这个问题?

堆栈跟踪:

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__ 钩子(Hook)来清理异步资源。你根本不能算它被调用,更不用说控制它何时被使用或者异步循环是否在那个时候仍然可用。你真的想明确地处理这个。

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

我会在此处将 API 实例设为上下文管理器:

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() 上等待,因此以上内容直接针对协程。

然后在你的主循环中使用它:

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

另一种选择是向 MyAPI 实例提供您自己的 session 对象,并在完成后自行负责关闭它:

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