Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/312.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Python中并行运行多个系统命令_Python - Fatal编程技术网

在Python中并行运行多个系统命令

在Python中并行运行多个系统命令,python,Python,我编写了一个简单的脚本,在一系列文件上执行系统命令。 为了加快速度,我希望并行运行它们,但不是一次运行所有命令——我需要控制同时运行命令的最大数量。 最简单的方法是什么?您需要的是一个线程池。有固定数量的线程可用于执行任务。当服务器不运行任务时,它会等待任务队列以获取要执行的新代码段 这是有道理的,但有评论说这还不算完整。可能还有其他包,但这是我找到的第一个包。如果您正在运行系统命令,您可以使用子流程模块创建流程实例,可以根据需要调用它们。应该不需要线程(它是非线程的),对于这个任务来说,多进程

我编写了一个简单的脚本,在一系列文件上执行系统命令。 为了加快速度,我希望并行运行它们,但不是一次运行所有命令——我需要控制同时运行命令的最大数量。
最简单的方法是什么?

您需要的是一个线程池。有固定数量的线程可用于执行任务。当服务器不运行任务时,它会等待任务队列以获取要执行的新代码段


这是有道理的,但有评论说这还不算完整。可能还有其他包,但这是我找到的第一个包。

如果您正在运行系统命令,您可以使用子流程模块创建流程实例,可以根据需要调用它们。应该不需要线程(它是非线程的),对于这个任务来说,多进程似乎有点过头了。

如果你还是调用子进程,我看不出有必要使用线程池。使用
子流程
模块的基本实现如下

import subprocess
import os
import time

files = <list of file names>
command = "/bin/touch"
processes = set()
max_processes = 5

for name in files:
    processes.add(subprocess.Popen([command, name]))
    if len(processes) >= max_processes:
        os.wait()
        processes.difference_update([
            p for p in processes if p.poll() is not None])
睡眠时间取决于子流程的预期执行时间。

您需要将对象与子流程组合。信号量是一个对象,它允许您限制在给定代码段中运行的线程数。在本例中,我们将使用信号量限制可以运行os.system调用的线程数

首先,我们导入所需的模块:

#!/usr/bin/python

import threading
import os
接下来,我们创建一个信号量对象。这里的数字4是一次可以获取信号量的线程数。这限制了一次可以运行的子进程的数量

semaphore = threading.Semaphore(4)
这个函数只是将对子进程的调用封装在对信号量的调用中

def run_command(cmd):
    semaphore.acquire()
    try:
        os.system(cmd)
    finally:
        semaphore.release()
如果您使用的是Python2.6+,这会变得更简单,因为您可以使用“with”语句来执行acquire和release调用

def run_command(cmd):
    with semaphore:
        os.system(cmd)
最后,为了证明这是按预期工作的,我们将调用“sleep 10”命令八次

for i in range(8):
    threading.Thread(target=run_command, args=("sleep 10", )).start()
使用“time”程序运行脚本表明,由于两组四次睡眠并行运行,只需20秒

aw@aw-laptop:~/personal/stackoverflow$ time python 4992400.py 

real    0m20.032s                                                                                                                                                                   
user    0m0.020s                                                                                                                                                                    
sys     0m0.008s 

斯文·马纳奇的回答几乎是正确的,但有一个问题。如果最后一个max_进程结束,主程序将尝试启动另一个进程,for循环将结束。这将关闭主进程,而主进程又可以关闭子进程。对我来说,这种行为发生在screen命令中

Linux中的代码如下所示(仅适用于python2.7):

导入子流程
导入操作系统
导入时间
文件=
command=“/bin/touch”
进程=集合()
最大进程数=5
对于文件中的名称:
processs.add(subprocess.Popen([command,name]))
如果len(进程)>=max_进程:
等等
进程.差异\u更新(
[p为进程中的p,如果p.poll()不是None])
#检查是否所有子进程都已关闭
对于流程中的p:
如果p.poll()为无:
p、 等等

此答案与此处的其他答案非常相似,但它使用列表而不是集合。 由于某种原因,当使用这些答案时,我得到了一个关于集合大小变化的运行时错误

from subprocess import PIPE
import subprocess
import time


def submit_job_max_len(job_list, max_processes):
  sleep_time = 0.1
  processes = list()
  for command in job_list:
    print 'running {n} processes. Submitting {proc}.'.format(n=len(processes),
        proc=str(command))
    processes.append(subprocess.Popen(command, shell=False, stdout=None,
      stdin=PIPE))
    while len(processes) >= max_processes:
      time.sleep(sleep_time)
      processes = [proc for proc in processes if proc.poll() is None]
  while len(processes) > 0:
    time.sleep(sleep_time)
    processes = [proc for proc in processes if proc.poll() is None]


cmd = '/bin/bash run_what.sh {n}'
job_list = ((cmd.format(n=i)).split() for i in range(100))
submit_job_max_len(job_list, max_processes=50)

我将Sven和Thuener的解决方案合并为一个等待后续进程的解决方案,并在其中一个进程崩溃时停止:

def removeFinishedProcesses(processes):
    """ given a list of (commandString, process), 
        remove those that have completed and return the result 
    """
    newProcs = []
    for pollCmd, pollProc in processes:
        retCode = pollProc.poll()
        if retCode==None:
            # still running
            newProcs.append((pollCmd, pollProc))
        elif retCode!=0:
            # failed
            raise Exception("Command %s failed" % pollCmd)
        else:
            logging.info("Command %s completed successfully" % pollCmd)
    return newProcs

def runCommands(commands, maxCpu):
            processes = []
            for command in commands:
                logging.info("Starting process %s" % command)
                proc =  subprocess.Popen(shlex.split(command))
                procTuple = (command, proc)
                processes.append(procTuple)
                while len(processes) >= maxCpu:
                    time.sleep(.2)
                    processes = removeFinishedProcesses(processes)

            # wait for all processes
            while len(processes)>0:
                time.sleep(0.5)
                processes = removeFinishedProcesses(processes)
            logging.info("All processes completed")

谢谢你对我的建议进行编码:)+1给你谢谢!这似乎是我所需要的——而且非常简单。不过,我应该指出,我使用的是Windows,似乎不支持os.wait()。有什么简单的解决方法吗?@user476983:Windows不允许等待任何子进程的终止。您可以通过每秒轮询所有子进程一次左右来解决此问题(取决于子进程的执行时间)。行“processs.difference_update(如果p.poll()不是None,则进程中的p代表p)”似乎有问题。这会导致“RuntimeError:在迭代期间设置更改的大小”@Mannaggia:您建议的代码具有不匹配的参数。将生成器表达式指定给临时变量不会产生任何影响。把它变成一个列表应该可以解决这个问题——我会更新答案。(此错误可能是由罕见的争用条件引起的。编辑代码并重试不会告诉您争用条件是否已修复。它可能只是在该特定运行中未发生,但在下一次运行中会再次发生。)@unholysampler:此问题与多线程或线程池无关。线程可能是给定问题的一种解决方案,但在我看来是一种糟糕的解决方案。我将再次删除这些标签。相关:@S.Lott。限制最大进程数似乎是合理的。假设你有10万个进程要启动,你想运行它们,一次生成所有进程吗?即使操作系统能够应对……。@S.Lott如果正在启动的进程是数据库密集型的,您可能会通过并行运行少量进程来提高速度,但在某一点后,争用将导致速度减慢。例如,如果系统命令是sftp,然后,您可能希望并行运行数量有限的进程。如果问题是引用一个系统命令,那么我对数据库的引用可能没有帮助,但这就是为什么我过去会遇到这种情况。我不喜欢为此使用线程。它们是完全不必要的——不管怎样,您都在启动子进程。尽管线程很便宜,而且信号量使跟踪运行进程的数量变得非常简单。是的,代码看起来很不错,尤其是在使用
with
语句时。一个缺点是,如果进程太多,你会先无条件地启动大量线程。我认为你应该删除它,并通过编辑将其添加到Sven的答案中。这是一张糟糕的表格吗?
荣耀属于那些回答问题的人。我在试你的
from subprocess import PIPE
import subprocess
import time


def submit_job_max_len(job_list, max_processes):
  sleep_time = 0.1
  processes = list()
  for command in job_list:
    print 'running {n} processes. Submitting {proc}.'.format(n=len(processes),
        proc=str(command))
    processes.append(subprocess.Popen(command, shell=False, stdout=None,
      stdin=PIPE))
    while len(processes) >= max_processes:
      time.sleep(sleep_time)
      processes = [proc for proc in processes if proc.poll() is None]
  while len(processes) > 0:
    time.sleep(sleep_time)
    processes = [proc for proc in processes if proc.poll() is None]


cmd = '/bin/bash run_what.sh {n}'
job_list = ((cmd.format(n=i)).split() for i in range(100))
submit_job_max_len(job_list, max_processes=50)
def removeFinishedProcesses(processes):
    """ given a list of (commandString, process), 
        remove those that have completed and return the result 
    """
    newProcs = []
    for pollCmd, pollProc in processes:
        retCode = pollProc.poll()
        if retCode==None:
            # still running
            newProcs.append((pollCmd, pollProc))
        elif retCode!=0:
            # failed
            raise Exception("Command %s failed" % pollCmd)
        else:
            logging.info("Command %s completed successfully" % pollCmd)
    return newProcs

def runCommands(commands, maxCpu):
            processes = []
            for command in commands:
                logging.info("Starting process %s" % command)
                proc =  subprocess.Popen(shlex.split(command))
                procTuple = (command, proc)
                processes.append(procTuple)
                while len(processes) >= maxCpu:
                    time.sleep(.2)
                    processes = removeFinishedProcesses(processes)

            # wait for all processes
            while len(processes)>0:
                time.sleep(0.5)
                processes = removeFinishedProcesses(processes)
            logging.info("All processes completed")