使用wait()实现Python子进程Popen的异步执行

3

我对Python的异步概念还不熟悉。但是我想按顺序调用2个函数,顺序很重要:

def func1():
    p = subprocess.Popen('ffmpeg -i in.webm out.mp4')
    p.wait()

def func2():
    os.remove('in.webm')

func1()
func2()

功能1包含一个子进程,用于转换视频文件,而功能2在此之后删除输入。
使用Popenp.wait()会强制同步执行,但代价是阻塞主线程。
不使用p.wait()Popen不会阻塞主线程,但使得ffmpeg完成其工作之前将调用功能2。

我想要实现与JavaScript Promise或async/await结构类似的效果,像这样:

async func1() {
    let response = await fetch('/api');
    let user = await response.json();
    return user;
}

func2(func1());

听起来像是一个琐碎的任务,但我在多线程、多进程和异步编程之间迷失了。
我如何让函数2等待函数1的输出而不会阻塞主线程?

如果ffmpeg -i in.webm out.mp4 && rm in.webm成功运行,您可以让shell为您完成它。 - Mark Setchell
@MarkSetchell 我想了解一下,ffmpeg纯粹是为演示目的而存在的概念。 - Igniter
在你的JS示例中,func2接收一个promise,如果它想要获取“user”,则必须等待它。您还没有指定func2是同步还是异步。能否请您详细说明JS示例,否则我不清楚您想要实现什么。 - user4815162342
@user4815162342 这个问题是如何使函数2等待函数1的输出而不阻塞主线程?可以考虑这样做:fetch('api').then(res => console.log(res.json()))。在Python中如何实现相同的功能呢? - Igniter
主线程将始终被阻塞,因为它可能正在运行 asyncio 事件循环。但是 事件循环 不会被阻塞,因为它将能够运行其他任务。就像 JavaScript 一样,asyncio 是单线程的。 - user4815162342
1个回答

3
要在asyncio程序中生成子进程,请使用asyncio的子进程原语,它们提供了类似于subprocess的接口,但公开了可等待协程。您的JavaScript示例的相当部分可能如下所示:
async def main():
    # Schedule func1() to run, but don't await it. (In JavaScript
    # it would be enough to just call func1().)
    asyncio.create_task(func1())

    print('we get here immediately')

    # Await something to keep the event loop spinning - without
    # this, the program would exit at this point. In real code
    # this would await an exit signal, server.wait_closed(), etc.
    await asyncio.Event().wait()

async def func1():
    # Like in JavaScript, await suspends the current function
    # until a result is available.
    res = await fetch('/api1', {})
    userid = (await res.json()).id
    asyncio.create_task(func2(userid))

async def func2(userid):
    res = await fetch('/api2', ...userid...)
    func3((await res.json()).url)

def func3(url):
    print('user photo', url)

asyncio.run(main())

@Igniter 你可以定义一个 func3,它执行 x = await func1(); func2(x),然后在 main 中调用 create_task(func3())。至于你的第二个问题,通常你只需要 await func1(),但是你的要求是主线程继续运行。由于 main() 表示一个(异步版本的)主线程,我让它睡眠,这样程序不会立即退出。 - user4815162342
是的,这是伪JS代码:https://pastebin.com/K72QTRTT - Igniter
@Igniter 我已经修订了答案,并提醒您在 asyncio 程序中使用 asyncio 子进程功能,而不是常规的 subprocess. - user4815162342
我认为有一个打字错误。你说 fun2,但实际上是 func2 - Hacker
@Hacker 已修复,谢谢! - user4815162342
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接