【发布时间】:2021-06-16 00:52:34
【问题描述】:
我已经构建了一个基于 aiohttp 的小蜘蛛。以下是一些缩写代码:
import asyncio
from aiohttp import ClientSession
from threading import Thread
class Spider:
def __init__(self, urls):
self.urls = urls
self.start()
async def fetch(self, session, url):
async with session.get(url) as response:
await self.handle_status(response) # undefined here for brevity
return await self.render_body(response) # undefined here for brevity
async def process_urls(self):
async with ClientSession() as session:
tasks = {self.fetch(session, url) for url in self.urls}
for task in asyncio.as_completed(tasks):
raw_data = await task
data = self.extract_data(*raw_data) # sync method undefined here for brevity
await self.store_data(data) # undefined here for brevity
def start(self) -> None:
try:
asyncio.run(self.process_urls())
except RuntimeError: # loop already running
x = Thread(target=asyncio.run, args=(self.process_urls(),))
x.start()
x.join()
start 方法旨在启动一个 asyncio 循环,但如果一个已经在运行,那么它会在一个新线程中启动一个新循环。
无论是否从现有循环运行,代码都有效。但如果从现有循环运行(例如使用 pytest.mark.asyncio()),我会收到以下警告:
RuntimeWarning:协程 'Spider.process_urls' 从未等待 x.join()
我应该关注这个警告吗?
有没有更好的方法来处理这个不会导致这个警告?
我尝试使用 loop = asyncio.get_running_loop() 和 loop.create_task(self.process_urls()) 而不是创建新线程,但这失败了:
RuntimeError: 协程忽略了 GeneratorExit
我尝试过nest-asyncio,但我的主要用例涉及另一个使用uvloop的库,因此nest-asyncio不兼容。
【问题讨论】:
-
我认为问题在于,在您的
try块中,您在引发RuntimeError之前创建了协程。所以任务永远不会被取消,因为RuntimeError在清理运行循环之前在run内部引发。也许在您的异常处理程序中手动取消您的任务以消除警告。 -
@thisisalsomypassword,我认为这不是问题所在。警告似乎指向 x.join 作为罪魁祸首,而不是 try 块中的 asyncio.run 。此外,
try: asyncio.get_running_loop(); except: asyncio.run(); else: Thread产生指向 x.join() 的相同错误 -
其实虽然列出了x.join(),但代码行号是
x = Thread这一行... -
RuntimeWarning在x.join()上引发,因为这是代码中循环终止的点。只有这样循环才能知道,您的协程从未被等待。除了这个警告之外,您的程序是否按预期工作?对于被忽略的GeneratorExit,我无话可说。也许完整的追溯会有所帮助。但是,我很确定将新任务附加到正在运行的循环中会比在线程中启动新循环更好。但这取决于你。
标签: python python-asyncio python-multithreading