首页 > 解决方案 > 使用 asyncio 在一个时间间隔内运行命令并在之后终止它

问题描述

我正在执行一个 shell 命令os.system()。我计划运行它 1 秒钟,然后如果超过时间就终止它。这是我尝试的测试。

import os, time, asyncio

async def cmd():
    os.system("my.py > my.txt") # this processes longer than 1 second

@asyncio.coroutine
def run():
    try:
        yield from asyncio.wait_for(cmd(), 1) # this should excute for more than 1 sec, and hence raise TimeoutError
        print("A")
    except asyncio.TimeoutError:
        os.system("killall python")
        print("B")
    
asyncio.run(run())

但结果始终是“A”,而 txt 是由 my.py 编写的。

我也试过:

import os, time, asyncio

async def cmd():
    os.system("my.py > my.txt") # longer than 1 sec

async def run():
    try:
        await asyncio.wait_for(cmd(), 1) # should raise TimeoutError
        print("A")
    except asyncio.TimeoutError:
        os.system("killall python")
        print("B")
    
asyncio.run(run())

结果相同的输出。代码有什么问题?我对 asyncio 很陌生,以前从未使用过它。提前致谢。很可能不是wait_for不能自动停止强制转换的问题,因为我killall python在第二部分中有一个,事实上,wait_for 函数永远不会引发超时错误,这就是问题所在。

标签: pythonpython-asynciopython-os

解决方案


os.system是一个阻塞操作,所以它不能很好地与asyncio. 每当您使用asyncio时,所有“更长”的操作都需要是异步的,否则您首先会失去使用asyncio的所有好处。

import asyncio

async def cmd():
    try:
        proc = await asyncio.create_subprocess_shell(
            "sleep 2 && date > my.txt",
            shell=True,
            stdout=asyncio.subprocess.DEVNULL,
            stderr=asyncio.subprocess.DEVNULL)
        await proc.communicate()

    except asyncio.CancelledError:
        proc.terminate()
        print("X")

async def run():
    try:
        await asyncio.wait_for(cmd(), 1)
        print("A")

    except asyncio.TimeoutError:
        print("B")

asyncio.run(run())

正如@dim 在评论中提到的,asyncio.create_subprocess_exec将异步启动一个外部命令。wait_for实际上会引发两个异常:TimeoutError在等待代码中和CancelledError在等待代码中。我们可以使用后者来实现操作被取消,terminate或者kill我们的子进程(因为这不是自动完成的)。


推荐阅读