等待具有多个并行作业的子进程结束

发布于 2024-09-08 15:25:16 字数 513 浏览 2 评论 0原文

我正在并行运行 python 中的一些子进程。我想等到每个子进程完成。我正在做一个不优雅的解决方案:

runcodes = ["script1.C", "script2.C"]
ps = []
for script in runcodes:
  args = ["root", "-l", "-q", script]
  p = subprocess.Popen(args)
  ps.append(p)
while True:
  ps_status = [p.poll() for p in ps]
  if all([x is not None for x in ps_status]):
    break

是否有一个类可以处理多个子进程?问题是 wait 方法阻塞了我的程序。

更新:我想显示计算过程中的进度:类似于“4/7 subprocess finish...”

如果您好奇root编译c ++脚本并执行它。

I'm running some subprocesses from python in parallel. I want to wait until every subprocess have finished. I'm doing a non elegant solution:

runcodes = ["script1.C", "script2.C"]
ps = []
for script in runcodes:
  args = ["root", "-l", "-q", script]
  p = subprocess.Popen(args)
  ps.append(p)
while True:
  ps_status = [p.poll() for p in ps]
  if all([x is not None for x in ps_status]):
    break

is there a class that can handle multiple subprocess? The problem is that the wait method block my program.

update: I want to show the progress during the computation: something like "4/7 subprocess finished..."

If you are curious root compile the c++ script and execute it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

止于盛夏 2024-09-15 15:25:16

如果您的平台不是 Windows,您可能可以选择子进程的标准输出管道。然后,您的应用程序将阻塞,直到:

  • 注册的文件描述符之一发生 I/O 事件(在本例中,我们对子进程的 stdout 管道上的挂起感兴趣)
  • poll 超时

使用 epoll 的非充实示例对于 Linux 2.6.xx:

import subprocess
import select

poller = select.epoll()
subprocs = {} #map stdout pipe's file descriptor to the Popen object

#spawn some processes
for i in xrange(5):
    subproc = subprocess.Popen(["mylongrunningproc"], stdout=subprocess.PIPE)
    subprocs[subproc.stdout.fileno()] = subproc
    poller.register(subproc.stdout, select.EPOLLHUP)

#loop that polls until completion
while True:
    for fd, flags in poller.poll(timeout=1): #never more than a second without a UI update
        done_proc = subprocs[fd]
        poller.unregister(fd)
        print "this proc is done! blah blah blah"
        ...  #do whatever
    #print a reassuring spinning progress widget
    ...
    #don't forget to break when all are done

If your platform is not Windows, you could probably select against the stdout pipes of your subprocesses. Your app will then block until either:

  • One of the registered file descriptors has an I/O event (in this case, we're interested in a hangup on the subprocess's stdout pipe)
  • The poll times out

Non-fleshed-out example using epoll with Linux 2.6.xx:

import subprocess
import select

poller = select.epoll()
subprocs = {} #map stdout pipe's file descriptor to the Popen object

#spawn some processes
for i in xrange(5):
    subproc = subprocess.Popen(["mylongrunningproc"], stdout=subprocess.PIPE)
    subprocs[subproc.stdout.fileno()] = subproc
    poller.register(subproc.stdout, select.EPOLLHUP)

#loop that polls until completion
while True:
    for fd, flags in poller.poll(timeout=1): #never more than a second without a UI update
        done_proc = subprocs[fd]
        poller.unregister(fd)
        print "this proc is done! blah blah blah"
        ...  #do whatever
    #print a reassuring spinning progress widget
    ...
    #don't forget to break when all are done
木森分化 2024-09-15 15:25:16

怎么样

import os, subprocess
runcodes = ["script1.C", "script2.C"]
ps = {}
for script in runcodes:
    args = ["root", "-l", "-q", script]
    p = subprocess.Popen(args)
    ps[p.pid] = p
print "Waiting for %d processes..." % len(ps)
while ps:
    pid, status = os.wait()
    if pid in ps:
        del ps[pid]
        print "Waiting for %d processes..." % len(ps)

How about

import os, subprocess
runcodes = ["script1.C", "script2.C"]
ps = {}
for script in runcodes:
    args = ["root", "-l", "-q", script]
    p = subprocess.Popen(args)
    ps[p.pid] = p
print "Waiting for %d processes..." % len(ps)
while ps:
    pid, status = os.wait()
    if pid in ps:
        del ps[pid]
        print "Waiting for %d processes..." % len(ps)
開玄 2024-09-15 15:25:16

您可以这样做:

runcodes = ["script1.C", "script2.C"]

ps = []
for script in runcodes:
    args = ["root", "-l", "-q", script]
    p = subprocess.Popen(args)
    ps.append(p)

for p in ps:
    p.wait()

这些进程将并行运行,最后您将等待所有进程。

You could do something like this:

runcodes = ["script1.C", "script2.C"]

ps = []
for script in runcodes:
    args = ["root", "-l", "-q", script]
    p = subprocess.Popen(args)
    ps.append(p)

for p in ps:
    p.wait()

The processes will run in parallel, and you'll wait for all of them at the end.

三生殊途 2024-09-15 15:25:16

这个答案与这个答案相关,并在下面使用类似的机制(基于select系统调用)称为asyncio
您可以在此处了解有关asyncio的更多信息。

当您的进程受 IO 限制时,Asyncio 非常有用。您的进程似乎是 IO 绑定的,至少在程序的这一部分中,花费大部分时间等待外部脚本完成,并且仅在其末尾打印一条消息。

以下代码应该适合您(也许需要一些细微的调整):

import asyncio

# The scripts you want to run concurrently
runcodes = ["script1.C", "script2.C"]

# An awaitable coroutine that calls your script
# and waits (non-blocking) until the script is done 
# to print a message
async def run_script(script):
    # You will need to adjust the arguments of create_subprocess_exec here
    # according to your needs
    p = await asyncio.create_subprocess_exec(script)
    await p.wait()
    print("Script", script, "is done")

# You create concurrent tasks for each script
# they will start in parallel as soon as they are
# created
async def main():
    tasks = []
    for script in runcodes:
        tasks.append(asyncio.create_task(run_script(script)))

    # You wait until all the tasks are done before 
    # continuing your program
    for task in tasks:
        await task

if __name__ == "__main__":
    asyncio.run(main())

一些详细的解释。

Asyncio 允许您通过交替执行各种异步任务并在所有异步任务都被阻塞时简单地等待,从而使用单个线程执行并发任务。

函数 run_script 是异步的,将使用与 subprocess.Popen 类似的机制调用您的脚本。这里的区别在于,返回的对象是可等待的,这意味着当等待函数被阻塞时,您可以跳转到其他对象。

您可以在此处阅读有关使用 asyncio 进行子流程管理的更多信息< /a>.您会注意到子进程的管理与“正常”Python 子处理非常相似。波彭的论点也类似。

请注意,这与线程不同。事实上,这个程序是单线程的。不要将 asyncio 与多线程混淆。它们是同时运行任务的两种不同方法(各有利弊)。

main 函数将创建多个任务,每个任务对应您要运行并等待它们的每个脚本。

重要的是这个 await 不会阻塞,同时也不会进行实时轮询。它将休眠,直到任何任务准备就绪。一旦任务准备就绪,执行就会返回到该任务,该任务可以打印有关您的消息的语句。

不过,在所有等待的任务完成之前,程序不会退出主函数。它将停留在由 asyncio.run 生成的等待函数的循环中,直到一切完成。

This answer is related to this answer and uses a similar mechanism underneath (based on the select syscall) called asyncio.
You can read more about asyncio here.

Asyncio is good when your process is IO-bound. Your process seems to be IO-bound, at least in this section of your program, spending most of its time waiting for the external scripts to complete and only printing a message at their end.

The following code should work for you (with some minor adjustments perhaps):

import asyncio

# The scripts you want to run concurrently
runcodes = ["script1.C", "script2.C"]

# An awaitable coroutine that calls your script
# and waits (non-blocking) until the script is done 
# to print a message
async def run_script(script):
    # You will need to adjust the arguments of create_subprocess_exec here
    # according to your needs
    p = await asyncio.create_subprocess_exec(script)
    await p.wait()
    print("Script", script, "is done")

# You create concurrent tasks for each script
# they will start in parallel as soon as they are
# created
async def main():
    tasks = []
    for script in runcodes:
        tasks.append(asyncio.create_task(run_script(script)))

    # You wait until all the tasks are done before 
    # continuing your program
    for task in tasks:
        await task

if __name__ == "__main__":
    asyncio.run(main())

Some detailed explanation.

Asyncio allows you to execute concurrent tasks with a single thread by alternating the various asynchronous tasks and simply waiting when all of them are blocked.

The function run_script is asynchronous and will call your script using a similar mechanism to subprocess.Popen. The difference here is that the returning object is awaitable meaning that you can jump to something else while the wait function is blocked.

You can read more about subprocess management with asyncio here. You will notice the management of the subprocess is very similar to the "normal" Python subprocessing. The arguments of the Popen are also similar.

Notice that this is different to threading. In fact, this program is single-threaded. Do not confuse asyncio with multi-threading. They are two different approaches to run tasks concurrently (with pros and cons).

The main function will create multiple tasks, one for each script you want to run and wait on them.

The important thing is that this await will not be blocking and at the same time it will not do live-polling. It will sleep until any of the tasks will be ready. Once a task is ready the execution returns to that task that can print a statement about your message.

The program will not exit the main function until all awaited tasks are done, though. It will stay within the loop of awaited functions generated by asyncio.run until everything is completed.

再见回来 2024-09-15 15:25:16

我认为答案不在于 python 代码或语言功能,而在于系统功能,请考虑以下解决方案:

runcodes = ["script1.C", "script2.C"]

Args = []
for script in runcodes:
    Args += " ".join(["root", "-l", "-q", script])


P = subprocess.Popen(" & ".join(Args))
P.wait()

I think the answer is not in python code or language features but it is in the system capabilities , consider this solution :

runcodes = ["script1.C", "script2.C"]

Args = []
for script in runcodes:
    Args += " ".join(["root", "-l", "-q", script])


P = subprocess.Popen(" & ".join(Args))
P.wait()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文