В конечном автомате Python asyncio, как предотвратить состояния за уничтоженными, которые имеют ожидающую задачу

У меня есть служебный класс, который планирует выполнение задания в timeout секунды:

class AsyncTimer:
    def __init__(self, timeout, callback):
        self._timeout = timeout
        self._callback = callback
        self._task = asyncio.ensure_future(self._job())

    async def _job(self):
        await asyncio.sleep(self._timeout)
        await self._callback()

У меня также есть конечный автомат, похожий на следующий:

class IState(metaclass=abc.ABCMeta):
    def __init__(self, machine):
        self._machine = machine

# The OnState can only transition into the TurningOffState, etc...
class OnState(IState):
    def __init__(self, machine):
        super().__init__(machine)
        print("Turned on!")     
    def turnOff(self):
        self.__machine.currentState = TurningOffState(self._machine)

class OffState(IState):
    def __init__(self, machine):
        super().__init__(machine)
        print("Turned off!")     
    def turnOn(self):
        self.__machine.currentState = TurningOnState(self._machine)

# Transition state that has no state transitions. automaticly transitions to `OnState` after 2 secconds
class TurningOnState(IState):
    async def _callback(self):
        self._machine.currentState = OnState(self._machine)
    def __init__(self, machine):
        super().__init__(machine)
        print("Turning on!")
        self.__timer = AsyncTimer(2, self._callback)

У меня есть объект, содержащий текущее активное состояние:

class FSM:
    def __init__(self):
        self.currentState = OffState(self)
    def __del__(self):
        print("destroying FSM")

Мой основной цикл выглядит так:

async def main():        
    fsm = FSM()
    fsm.currentState.turnOn()
    # fsm gets destroyed here, along with currently contained state which may have a pending task. How do I fix this?


loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
    loop.run_until_complete(main())
finally:
    loop.run_until_complete(loop.shutdown_asyncgens())
    loop.close()

Это выводит:

Turned Off!
Turning On!
destroying FSM
Task was destroyed but it is pending!
task: <Task pending coro=<AsyncTimer._job() done, defined at pythreadtest.py:11> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x7f0c1d891258>()]>>

Я почти уверен, что проблема в том, что FSM уничтожается после включения запроса вместе с текущим состоянием, которое может иметь отложенную задачу.

Как я мог это исправить? Есть ли что-то, что я мог бы добавить в деструктор, чтобы дождаться завершения всех ожидающих задач? Как я мог это сделать? Есть ли другие проблемы с моим дизайном?

Благодарность

редактировать

Я пробовал добавить sleepпосле запроса на переход. Это решает мою проблему, но было бы хорошо, если бы конечный автомат мог атомарноsleep при уничтожении, пока не будут выполнены все задачи.

fsm = DoorFSM()
fsm.currentState.openDoor()
await asyncio.sleep(3)

edit2

Другое решение - сделать task из AsyncTimerpublic, а затем выставить это в переходных состояниях. то есть:

def __init__(self, machine):
    super().__init__(machine)
    print("Turning On")
    self.__timer = AsyncTimer(2, self._callback)
    self.task = self.__timer.task

Затем основной цикл может дождаться завершения этой задачи с помощью wait

fsm = DoorFSM()
fsm.currentState.openDoor()
await asyncio.wait({fsm.currentState.task})

Но я бы предпочел, чтобы я мог поместить это в деструктор FSM, но это не позволяет мне поместить ожидание в деструктор.

0 ответов

Другие вопросы по тегам