在线程中停止asyncio

时间:2017-11-06 09:28:57

标签: python python-3.x python-asyncio

我有一个线程可以启动(并最终停止)asyncio循环,如下所示:

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.CancelledError ...在stop方法中的任何地方捕捉这些似乎没有帮助。

如果我运行未标有@pytest.mark.asyncio的测试代码,我会收到一条消息Task was destroyed but it is pending!

我做错了什么?

1 个答案:

答案 0 :(得分:1)

我们在这里有几个问题。

  1. Task.cancel()在couroutine中引发了一个asyncio.CancelledError()。你应该添加一个" try / exec CancelledError"在你的协程中处理这个例外。

  2. 另一种方法是抑制def stop中的CancelledError异常:

    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)
    
  3. 记得用

    关闭所有异步生成器
    loop.run_until_complete(loop.shutdown_asyncgens())