首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Python Asyncio - RuntimeError:无法关闭正在运行的事件循环

Python Asyncio - RuntimeError:无法关闭正在运行的事件循环
EN

Stack Overflow用户
提问于 2018-09-04 01:39:27
回答 1查看 0关注 0票数 0

我正在尝试解决此错误:RuntimeError: Cannot close a running event loop在我的asyncio进程中。我相信它正在发生,因为当任务仍在等待时出现故障,然后我尝试关闭事件循环。我想我需要在关闭事件循环之前等待剩余的响应,但我不确定如何在我的特定情况下正确完成。

代码语言:javascript
复制
 def start_job(self):

        if self.auth_expire_timestamp < get_timestamp():
            api_obj = api_handler.Api('Api Name', self.dbObj)
            self.api_auth_resp = api_obj.get_auth_response()
            self.api_attr = api_obj.get_attributes()


        try:
            self.queue_manager(self.do_stuff(json_data))
        except aiohttp.ServerDisconnectedError as e:
            logging.info("Reconnecting...")
            api_obj = api_handler.Api('API Name', self.dbObj)
            self.api_auth_resp = api_obj.get_auth_response()
            self.api_attr = api_obj.get_attributes()
            self.run_eligibility()

async def do_stuff(self, data):

    tasks = []

    async with aiohttp.ClientSession() as session:
        for row in data:
            task = asyncio.ensure_future(self.async_post('url', session, row))
            tasks.append(task)
        result = await asyncio.gather(*tasks)
    self.load_results(result)


def queue_manager(self, method):
    self.loop = asyncio.get_event_loop()
    future = asyncio.ensure_future(method)
    self.loop.run_until_complete(future)


async def async_post(self, resource, session, data):
        async with session.post(self.api_attr.api_endpoint + resource, headers=self.headers, data=data) as response:
            resp = []
            try:
                headers = response.headers['foo']
                content = await response.read()
                resp.append(headers)
                resp.append(content)
            except KeyError as e:
                logging.error('KeyError at async_post response')
                logging.error(e)
        return resp


def shutdown(self):
    //need to do something here to await the remaining tasks and then I need to re-start a new event loop, which i think i can do, just don't know how to appropriately stop the current one.
    self.loop.close() 
    return True

如何处理错误并正确关闭事件循环,以便我可以启动一个新的并基本上重新启动整个程序并继续。

编辑:

基于这个SO答案,这就是我现在正在尝试的。不幸的是,这个错误很少发生,所以除非我可以强制它,否则我将不得不等待,看看它是否有效。在我的queue_manager方法中,我将其更改为:

代码语言:javascript
复制
try:
   self.loop.run_until_complete(future)
except Exception as e:
   future.cancel()
   self.loop.run_until_complete(future)
   future.exception()

更新:

我摆脱了shutdown()方法,并将其添加到我的queue_manager()方法,它似乎工作没有问题:

代码语言:javascript
复制
  try:
        self.loop.run_until_complete(future)
    except Exception as e:
        future.cancel()
        self.check_in_records()
        self.reconnect()
        self.start_job()
        future.exception()
EN

回答 1

Stack Overflow用户

发布于 2018-09-04 11:30:11

要回答最初声明的问题,不需要close()运行循环,可以为整个程序重用相同的循环。

鉴于更新中的代码,您queue_manager可能看起来像这样:

代码语言:javascript
复制
try:
    self.loop.run_until_complete(future)
except Exception as e:
    self.check_in_records()
    self.reconnect()
    self.start_job()

取消future是没有必要的,据我所知,没有效果。这与具体反应的引用答案不同KeyboardInterrupt,特别是因为它是由asyncio本身引发的。KeyboardInterrupt可以在run_until_complete没有实际完成的未来的情况下传播。Ctrl-C在asyncio中正确处理是非常困难甚至是不可能的(详见此处),但幸运的是,这个问题根本不是关于Ctrl-C协程引发的异常。(注意,KeyboardInterrupt不继承自Exception,因此在Ctrl-C除了body之外甚至不会执行。)

我正在取消未来,因为在这种情况下还有剩余的任务待定,我想基本上删除这些任务并开始一个新的事件循环。

这是一个正确的事情,但(更新的)问题中的代码只取消了一个已经传递给它的未来run_until_complete。回想一下,future是一个占位符,表示将在稍后提供的结果值。提供值后,可以通过调用来检索它future.result()。如果未来的“价值”是例外,future.result()则会引发该例外。run_until_complete只要给定的未来生成一个值,它就会运行事件循环,然后返回该值。如果“价值”实际上是提高的例外,那么run_until_complete将重新提高它。例如:

代码语言:javascript
复制
loop = asyncio.get_event_loop()
fut = loop.create_future()
loop.call_soon(fut.set_exception, ZeroDivisionError)
loop.run_until_complete(fut)  # raises ZeroDivisionError

当有问题的未来实际上是Task一个特定于asyncio的对象,它将协程包装成a时Future,这种未来的结果就是协同程序返回的对象。如果协同程序引发异常,那么检索结果将重新引发它,因此将run_until_complete

代码语言:javascript
复制
async def fail():
    1/0

loop = asyncio.get_event_loop()
fut = loop.create_task(fail())
loop.run_until_complete(fut)  # raises ZeroDivisionError

在处理任务时,run_until_complete完成意味着协程已完成,返回值或引发异常,如run_until_complete返回或提升所确定的。

另一方面,取消任务意味着下次恢复任务时await,暂停它的任务将提升CancelledError而不是值。错误将传播出任务,并且CancelledError任何等待它的代码都会观察到错误。但是,如果在cancel()调用时协程已经完成,则cancel()无法执行任何操作,因为没有挂起await的注入CancelledError

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/-100006098

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档