【问题标题】:Asynchronous execution of Python subprocess.Popen with wait()Python subprocess.Popen 与 wait() 的异步执行
【发布时间】:2020-07-18 10:58:27
【问题描述】:

我是 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() 的 987654324@ 不会阻止它,但会在 ffmpeg 完成其工作之前调用函数 2。

我想实现类似于 Javascript 承诺或 async/await 结构的东西,如下所示:

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

func2(func1());

听起来像是一项微不足道的任务,但我在多线程、多处理和异步之间迷失了。
如何让函数 2 在不阻塞主线程的情况下等待函数 1 的输出?

【问题讨论】:

  • 如果ffmpeg 使用ffmpeg -i in.webm out.mp4 && rm in.webm 成功运行,你可以让shell 为你做这件事
  • @MarkSetchell 我想了解一下这个概念,ffmpeg 纯粹是为了演示目的
  • 在您的 JS 示例中,func2 收到一个承诺,如果它想获得“用户”,它必须等待。您也没有指定 func2 是同步还是异步。您能否充实 JS 示例,否则(对我而言)您想要实现的目标并不明显。
  • @user4815162342 问题是如何让函数 2 在不阻塞主线程的情况下等待函数 1 的输出?想想这个:fetch('api').then(res => console.log(res.json()))。如何在 Python 中实现相同的功能?
  • 主线程将始终被阻塞,因为它可能正在运行 asyncio 事件循环。但是 事件循环 不会被阻塞,因为它可以运行其他任务。就像 JavaScrpit 一样,asyncio 是单线程的。

标签: python subprocess python-asyncio python-multithreading popen


【解决方案1】:

要在 asyncio 程序中生成子进程,请使用 asyncio's subprocess primitives,它提供类似于 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(fun2(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 两者都不是。它是单线程和协作的,就像 JavaScript 中的那样。在 Python 中,实现使用引擎盖下的生成器,参见例如this lecture了解详情。
  • 知道了,很有趣!但是如何在 main() 中调用 func2,并使用 func1 的实际输出作为其参数?我的意思是你如何使用来自 func1 的 user 数据而不是 sleep(50) 来调用 func2?
  • @Igniter 您可以定义一个执行x = await func1(); func2(x)func3 并在main 中调用create_task(func3())。至于您的第二个问题,通常您只需await func1(),但我的要求是主线程继续运行。因为main() 代表一个(异步版本的)主线程,所以我让它休眠,所以程序不会立即退出。
  • 类似的东西? pastebin.com/VLv01stK
猜你喜欢
  • 2011-11-26
  • 1970-01-01
  • 2017-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-19
  • 2018-01-11
  • 2012-02-28
相关资源
最近更新 更多