简体   繁体   English

RuntimeError:无法关闭正在运行的事件循环

[英]RuntimeError: Cannot close a running event loop

I'm trying to resolve this error: RuntimeError: Cannot close a running event loop in my asyncio process.我正在尝试解决此错误: RuntimeError: Cannot close a running event loop in my asyncio process。 I believe it's happening because there's a failure while tasks are still pending, and then I try to close the event loop.我相信它正在发生,因为在任务仍未决时出现故障,然后我尝试关闭事件循环。 I'm thinking I need to await the remaining responses prior to closing the event loop, but I'm not sure how to accomplish that correctly in my specific situation.我在想我需要在关闭事件循环之前等待剩余的响应,但我不确定如何在我的特定情况下正确地完成它。

 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

How can I handle the error and properly close the event loop so I can start a new one and essentially re-boot the whole program and continue on.我如何处理错误并正确关闭事件循环,以便我可以启动一个新的事件循环并重新启动整个程序并继续。

EDIT:编辑:

This is what I'm trying now, based on this SO answer .这就是我现在正在尝试的,基于这个 SO answer Unfortunately, this error only happens rarely, so unless I can force it, i will have to wait and see if it works.不幸的是,这个错误很少发生,所以除非我能强制执行,否则我将不得不等待,看看它是否有效。 In my queue_manager method I changed it to this:在我的queue_manager方法中,我将其更改为:

try:
    self.loop.run_until_complete(future)
except Exception as e:
    future.cancel()
    self.loop.run_until_complete(future)
    future.exception()

UPDATE:更新:

I got rid of the shutdown() method and added this to my queue_manager() method instead and it seems to be working without issue:我摆脱了shutdown()方法并将其添加到我的queue_manager()方法中,它似乎工作正常:

try:
    self.loop.run_until_complete(future)
except Exception as e:
    future.cancel()
    self.check_in_records()
    self.reconnect()
    self.start_job()
    future.exception()

To answer the question as originally stated, there is no need to close() a running loop, you can reuse the same loop for the whole program. 要回答最初陈述的问题,不需要close()一个运行循环,你可以为整个程序重用相同的循环。

Given the code in the update, your queue_manager could look like this: 鉴于更新中的代码,您的queue_manager可能如下所示:

try:
    self.loop.run_until_complete(future)
except Exception as e:
    self.check_in_records()
    self.reconnect()
    self.start_job()

Cancelling future is not necessary and as far as I can tell has no effect. 取消future是没有必要的,据我所知,没有效果。 This is different from the referenced answer which specifically reacts to KeyboardInterrupt , special because it is raised by asyncio itself. 这与专门对KeyboardInterrupt作出反应的引用答案不同,特别是因为它是由asyncio本身引发的。 KeyboardInterrupt can be propagated by run_until_complete without the future having actually completed. KeyboardInterrupt可以通过run_until_complete传播,而不会在将来实际完成。 Handling Ctrl-C correctly in asyncio is very hard or even impossible (see here for details), but fortunately the question is not about Ctrl-C at all, it is about exceptions raised by the coroutine. 在asyncio中正确处理Ctrl-C非常困难甚至不可能(详见此处 ),但幸运的是问题根本不是关于Ctrl-C ,而是关于协同程序引发的异常。 (Note that KeyboardInterrupt doesn't inherit from Exception , so in case of Ctrl-C the except body won't even execute.) (请注意, KeyboardInterrupt不会从Exception继承,因此在Ctrl-C的情况下,除了body之外甚至都不会执行。)

I was canceling the future because in this instance there are remaining tasks pending and i want to essentially remove those tasks and start a fresh event loop. 我正在取消未来,因为在这种情况下还有剩余的任务待定,我想基本上删除这些任务并开始一个新的事件循环。

This is a correct thing to want to do, but the code in the (updated) question is only canceling a single future, the one already passed to run_until_complete . 这是一件正确的事情,但(更新的)问题中的代码只取消了一个未来,即已经传递给run_until_complete Recall that a future is a placeholder for a result value that will be provided at a later point. 回想一下,future是一个占位符,表示将在稍后提供的结果值。 Once the value is provided, it can be retrieved by calling future.result() . 提供值后,可以通过调用future.result()来检索它。 If the "value" of the future is an exception, future.result() will raise that exception. 如果未来的“值”是一个例外,则future.result()将引发该异常。 run_until_complete has the contract that it will run the event loop for as long as it takes for the given future to produce a value, and then it returns that value. run_until_complete具有以下约定:只要给定的future将生成一个值,它将运行事件循环,然后返回该值。 If the "value" is in fact an exception to raise, then run_until_complete will re-raise it. 如果“value”实际上是引发的异常,则run_until_complete将重新引发它。 For example: 例如:

loop = asyncio.get_event_loop()
fut = loop.create_future()
loop.call_soon(fut.set_exception, ZeroDivisionError)
# raises ZeroDivisionError, as that is the future's result,
# manually set
loop.run_until_complete(fut)

When the future in question is in fact a Task , an asyncio-specific object that wraps a coroutine into a Future , the result of such future is the object returned by the coroutine. 当有问题的未来实际上是一个Task ,一个将协程包装到Future的特定于asyncio的对象时,这种未来的结果就是协同程序返回的对象。 If the coroutine raises an exception, then retrieving the result will re-raise it, and so will run_until_complete : 如果协同程序引发异常,那么检索结果将重新引发它, run_until_complete也是如此:

async def fail():
    1/0

loop = asyncio.get_event_loop()
fut = loop.create_task(fail())
# raises ZeroDivisionError, as that is the future's result,
# because the coroutine raises it
loop.run_until_complete(fut)

When dealing with a task, run_until_complete finishing means that the coroutine has finished as well, having either returned a value or raised an exception, as determined by run_until_complete returning or raising. 在处理任务时, run_until_complete结束意味着协程已完成,返回值或引发异常,由run_until_complete返回或引发确定。

On the other hand, cancelling a task works by arranging for the task to be resumed and the await expression that suspended it to raise CancelledError . 另一方面,取消任务的工作原理是安排要恢复的任务和await挂起它的表达式以引发CancelledError Unless the task specifically catches and suppresses this exception (which well-behaved asyncio code is not supposed to do), the task will stop executing and the CancelledError will become its result. 除非该任务专门捕获并抑制此异常(表现良好的asyncio代码不应该执行),否则任务将停止执行, CancelledError将成为其结果。 However, if the coroutine is already finished when cancel() is called, then cancel() cannot do anything because there is no pending await to inject CancelledError into. 但是,如果在调用cancel()时协程已经完成,则cancel()无法执行任何操作,因为没有挂起awaitCancelledError注入。

I got the same error below:我在下面遇到了同样的错误:

RuntimeError: Cannot close a running event loop RuntimeError:无法关闭正在运行的事件循环

When I called loop.close() in test() as shown below:当我在test()中调用loop.close()时,如下所示:

import asyncio

async def test(loop):
    print("Test")
    loop.stop()
    loop.close() # Here

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)

loop.create_task(test(loop))

loop.run_forever()

So, I used loop.close() after loop.run_forever() with try: and finally : as shown below, then the error was solved:所以,我在loop.close()之后使用了loop.run_forever()try: finally如下所示,然后错误就解决了:

import asyncio

async def test(loop):
    print("Test")
    loop.stop()

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)

loop.create_task(test(loop))

try:
    loop.run_forever()
finally:
    loop.close() # Here

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 (Python) Discord bot 代码返回“RuntimeError:无法关闭正在运行的事件循环” - (Python) Discord bot code returns "RuntimeError: Cannot close a running event loop" 无法关闭正在运行的事件循环 - Cannot close a running event loop &#39;在另一个循环正在运行时无法运行事件循环&#39;) RuntimeError websockets? - 'Cannot run the event loop while another loop is running') RuntimeError websockets? RuntimeError:无法从正在运行的事件循环中调用 asyncio.run() - RuntimeError: asyncio.run() cannot be called from a running event loop Spyder:无法关闭正在运行的事件循环 - Spyder: Cannot close a running event loop Python:“无法关闭正在运行的事件循环”Asyncio - Python: "Cannot close a running event loop" Asyncio 使用 Tornado 发出同步请求。 RuntimeError:在另一个循环正在运行时无法运行事件循环 - Making synchronous requests using Tornado. RuntimeError: Cannot run the event loop while another loop is running 运行时错误:此事件循环已在 python 中运行 - RuntimeError: This event loop is already running in python python3.8 RuntimeError:没有正在运行的事件循环 - python3.8 RuntimeError: no running event loop Python RuntimeError:此事件循环已在运行 - Python RuntimeError: This event loop is already running
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM