【发布时间】:2019-01-26 22:20:25
【问题描述】:
我正在使用 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 命令)并等待它们完成。这工作正常,两个命令并行运行。
主要问题是停止程序。我想在程序收到 SIGINT 或 KeyboardInterrupt 时运行 stop 方法,它首先停止 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 方法?
【问题讨论】:
标签: python python-asyncio python-3.7