【问题标题】:Asyncio exception handler: not getting called until event loop thread stoppedAsyncio 异常处理程序:直到事件循环线程停止才被调用
【发布时间】:2017-11-21 12:40:06
【问题描述】:

我正在我的 asyncio 事件循环上设置一个异常处理程序。但是,它似乎直到事件循环线程停止才被调用。例如,考虑以下代码:

def exception_handler(loop, context):
    print('Exception handler called')

loop = asyncio.get_event_loop()

loop.set_exception_handler(exception_handler)

thread = Thread(target=loop.run_forever)
thread.start()

async def run():
    raise RuntimeError()

asyncio.run_coroutine_threadsafe(run(), loop)

loop.call_soon_threadsafe(loop.stop, loop)

thread.join()

正如我们所料,这段代码打印出“Exception handler called”。但是,如果我删除关闭事件循环 (loop.call_soon_threadsafe(loop.stop, loop)) 的行,它将不再打印任何内容。

我对此有几个问题:

  • 我在这里做错了吗?

  • 有谁知道这是否是异步异常处理程序的预期行为?我找不到任何记录这一点的东西,这对我来说似乎有点奇怪。

我非常希望有一个长时间运行的事件循环来记录其协程中发生的错误,因此当前的行为对我来说似乎有问题。

【问题讨论】:

  • 您是否提出过任何错误? asyncio.run_coroutine_threadsafe(run(), loop) 你在没有线程的情况下启动了run function(用于函数运行run != run()
  • @dsgdfg:我不太清楚你的意思,但请记住,run 是一个协程,所以调用 run() 不会运行它。

标签: python python-asyncio


【解决方案1】:

上面的代码有几个问题:

  • stop() 不需要参数
  • 程序在协程执行之前结束(在它之前调用了stop())。

这里是固定代码(没有异常和异常处理程序):

import asyncio
from threading import Thread


async def coro():
    print("in coro")
    return 42


loop = asyncio.get_event_loop()
thread = Thread(target=loop.run_forever)
thread.start()

fut = asyncio.run_coroutine_threadsafe(coro(), loop)

print(fut.result())

loop.call_soon_threadsafe(loop.stop)

thread.join()

call_soon_threadsafe() 返回一个持有异常的未来对象(它不会到达默认的异常处理程序):

import asyncio
from pprint import pprint
from threading import Thread


def exception_handler(loop, context):
    print('Exception handler called')
    pprint(context)


loop = asyncio.get_event_loop()

loop.set_exception_handler(exception_handler)

thread = Thread(target=loop.run_forever)
thread.start()


async def coro():
    print("coro")
    raise RuntimeError("BOOM!")


fut = asyncio.run_coroutine_threadsafe(coro(), loop)
try:
    print("success:", fut.result())
except:
    print("exception:", fut.exception())

loop.call_soon_threadsafe(loop.stop)

thread.join()

但是,使用 create_task()ensure_future() 调用的协程将调用 exception_handler:

async def coro2():
    print("coro2")
    raise RuntimeError("BOOM2!")


async def coro():
    loop.create_task(coro2())
    print("coro")
    raise RuntimeError("BOOM!")

您可以使用它来创建一个小型包装器:

async def boom(x):
    print("boom", x)
    raise RuntimeError("BOOM!")


async def call_later(coro, *args, **kwargs):
    loop.create_task(coro(*args, **kwargs))
    return "ok"


fut = asyncio.run_coroutine_threadsafe(call_later(boom, 7), loop)

但是,您可能应该考虑使用Queue 来与您的线程进行通信。

【讨论】:

  • 太好了,谢谢 Udi。总而言之,实际上是我对loop.call_soon_threadsafe(loop.stop, loop) 的错误调用导致了异常处理程序被调用,而不是run() 协程。因此,asyncio.run_coroutine_threadsafe 似乎永远不会导致调用异常处理程序。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-08
  • 2023-02-09
  • 1970-01-01
  • 1970-01-01
  • 2016-08-27
相关资源
最近更新 更多