Python 停止线程中的异步IO
我有一个线程可以启动(并最终停止)异步IO循环,如下所示:Python 停止线程中的异步IO,python,python-3.x,python-asyncio,Python,Python 3.x,Python Asyncio,我有一个线程可以启动(并最终停止)异步IO循环,如下所示: class Ook(Thread): […] def run(self): try: self._logger.debug("Asyncio loop runs forever.") self.loop.run_forever() except Exception as ex: # We do want to se
class Ook(Thread):
[…]
def run(self):
try:
self._logger.debug("Asyncio loop runs forever.")
self.loop.run_forever()
except Exception as ex:
# We do want to see ALL unhandled exceptions here.
self._logger.error("Exception raised: %s", ex)
self._logger.exception(ex)
finally:
# Stop the loop!
self._logger.warn('Closing asyncio event loop.')
self.loop.run_until_complete(self.loop.shutdown_asyncgens())
self.loop.close()
def stop(self):
self._logger.info("Thread has been asked to stop!")
if self.loop.is_running():
self._logger.debug("Asked running asyncio loop to stop.")
for task in asyncio.Task.all_tasks():
self.loop.call_soon_threadsafe(task.cancel)
self.loop.call_soon_threadsafe(self.loop.stop)
一个愚蠢的(?)单元测试来检查工作是否正常
@pytest.mark.asyncio
async def test_start_and_stop_thread():
sut = Ook()
sut.start()
if sut.isAlive():
sut.stop()
sut.join()
assert not sut.isAlive()
assert not sut.loop.is_running()
这不起作用,因为引发了asyncio.cancelederror
…在stop
方法中的任何位置捕捉这些错误似乎都没有帮助
如果我运行的测试代码没有标记为@pytest.mark.asyncio
,我会收到一条消息说任务已销毁,但它已挂起代码>
我做错了什么?我们这里有几个问题
Task.cancel()在例程中引发asyncio.CancelleError()。您应该在协同程序中添加一个“try/exec cancelederror”来处理该异常
另一种方法是抑制def stop中的Cancelled Error异常:
from asyncio import CancelledError
from contextlib import suppress
def stop(self):
self._logger.info("Thread has been asked to stop!")
if self.loop.is_running():
self._logger.debug("Asked running asyncio loop to stop.")
self.loop.call_soon_threadsafe(self.loop.stop)
for task in asyncio.Task.all_tasks():
task.cancel()
with suppress(CancelledError):
loop.run_until_complete(task)
请记住关闭所有异步发电机
loop.run_until_complete(loop.shutdown_asyncgens())
尝试等待几秒钟,然后检查isAlive
?@Sraw为什么会有帮助?在任何情况下,我都尝试过,但没有。因为我认为这可能是由于在run
完成之前调用stop
引起的。不幸的是,它似乎不是。@Sraw这是一个公平的观点。对于asyncio.task.all_tasks(self.loop)中的task,它应该是。如果没有参数,则返回的数组为空。