Закрыть асинхронный цикл на KeyboardInterrupt - Выполнить процедуру остановки - PullRequest
0 голосов
/ 27 января 2019

Я использую python для создания скрипта, который запускается и взаимодействует с некоторыми процессами одновременно.Для этого я использую asyncio для реализации этого параллелизма.Основная проблема заключается в том, как запустить другую процедуру очистки при возникновении KeyboardInterrupt или SIGINT.

Вот пример кода, который я написал, чтобы показать проблему:

import asyncio
import logging
import signal
from time import sleep


class Process:
    async def start(self, arguments):
        self._process = await asyncio.create_subprocess_exec("/bin/bash", *arguments)

        return await self._process.wait()

    async def stop(self):
        self._process.terminate()


class BackgroundTask:

    async def start(self):
        # Very important process which needs to run while process 2 is running
        self._process1 = Process()
        self._process1_task = asyncio.create_task(self._process1.start(["-c", "sleep 100"]))

        self._process2 = Process()
        self._process2_task = asyncio.create_task(self._process2.start(["-c", "sleep 50"]))

        await asyncio.wait([self._process1_task, self._process2_task], return_when=asyncio.ALL_COMPLETED)

    async def stop(self):
        # Stop process
        await self._process1.stop()

        # Call a cleanup process which cleans up process 1
        cleanup_process = Process()
        await cleanup_process.start(["-c", "sleep 10"])

        # After that we can stop our second process
        await self._process2.stop()


backgroundTask = BackgroundTask()


async def main():
    await asyncio.create_task(backgroundTask.start())


logging.basicConfig(level=logging.DEBUG)
asyncio.run(main(), debug=True)

Этот код создает фоновую задачукоторый запускает два процесса (в этом примере две команды bash sleep) и ожидает их завершения.Это работает нормально, и обе команды работают параллельно.

Основная проблема - процедура остановки.Я хотел бы запустить метод stop, когда программа получает SIGINT или KeyboardInterrupt, который сначала останавливает process1, затем запускает метод очистки и затем останавливает process2.Это необходимо, потому что команда очистки зависит от process2.

Что я пробовал (вместо asyncio.run () и async main):

def main():
    try:
        asyncio.get_event_loop().run_until_complete(backgroundTask.start())
    except KeyboardInterrupt:
        asyncio.get_event_loop().run_until_complete(backgroundTask.stop())

main()

Это, конечно, нескольконе работает должным образом, потому что, как только возникает исключение KeyboardInterrupt, задача backgroundTask.start отменяется, и backgroundTask.stop запускается в главном цикле, поэтому мои процессы отменяются и не могут быть остановлены должным образом.

Так есть ли способ обнаружить KeyboardInterrupt без отмены текущего основного цикла и запустить вместо этого мой метод backgroundTask.stop?

...