我有一个将在其中包含aiohttp.ClientSession对象的类。
通常在你使用时
async with aiohttp.ClientSession() as session:
# some code
会话将在调用会话的__aexit__方法后关闭。
我不能使用上下文管理器,因为我希望在对象的整个生命周期内保持会话持久。
这有效:
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__
钩子来清理异步资源。您根本无法计算它被调用,更不用说控制何时使用它或者当时异步循环仍然可用。你真的想明确处理这个问题。
使用finally
处理程序,使API成为异步上下文管理器,或以其他方式显式清除退出时的资源; with
和async with
语句基本上是为了封装传统上在finally
块中处理的资源清理。
我在这里将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
实例,并在完成后自行承担关闭它的责任:
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()