Я хочу иметь возможность запускать один скрипт (например, скрипт python или что-то еще, что имеет наибольшее значение для этих вещей), который будет делать что-то эквивалентное примерно так:

$ program -i input -k 0.1 -m 0.01 &
$ program -i input -k 0.2 -m 0.22 &
$ program -i input -k 0.3 -m 3.03 &

Я хотел бы настроить любые параметры, которые я хочу в сценарии, и просто набрать «python script.py» и запустить любое количество экземпляров программы, которое я хочу, и все они будут выполняться параллельно в фоновом режиме. Я видел, как люди рекомендуют subprocess.Popen(), но я не знаю, как настроить его для параллельного выполнения нескольких заданий. Как мне этого добиться?

Я думал составить список команд и перебирать их, но 1) я думаю, что он будет ждать завершения первого задания, прежде чем запускать второе задание в очереди, и 2) это кажется неэффективным.

Кроме того, если писать это как сценарий Python не имеет смысла, дайте мне знать, что было бы лучшей альтернативой; Я никогда не делал этого раньше и не знаю, что делаю.

1
rcpi 5 Ноя 2018 в 03:01

1 ответ

Лучший ответ

Простое решение Python с использованием модуля multiprocessing может выглядеть так:

import os
import multiprocessing

def run_command(cmd):
    """ execute cmd via the shell. """
    print("starting `{}` ...".format(cmd))
    os.system(cmd)
    print("end `{}`".format(cmd))

def run_commands(commands, n_parallel):
    """ run commands (up to n_parallel in parallel). """
    worker = multiprocessing.Pool(n_parallel)
    worker.map(run_command, commands)

if __name__ == "__main__":
    run_commands([
        "program -i input -k 0.1 -m 0.01",
        "program -i input -k 0.2 -m 0.22",
        "program -i input -k 0.3 -m 3.03",
    ], n_parallel=2)

Центральным элементом является map() метод multiprocessing.Pool. Эта функция принимает список входных значений (в нашем случае команд оболочки) и передает их в другую функцию, каждая в своем собственном процессе, вплоть до размера пула параллельных процессов.

Однако у этого простого сценария есть один недостаток: вывод команд оболочки, выполняемых параллельно, будет смешанным. Этого можно избежать, захватив и вернув вывод команд в run_command(), например. используя subprocess.check_output() вместо os.system():

import subprocess

def run_command(cmd):
    try:
        output = subprocess.check_output(cmd, shell=True)
    except subprocess.CalledProcessError:
        output = "ERROR in {}".format(cmd)
    return output

multiprocessing.Pool.map() будет собирать и возвращать эти выходные данные в виде списка, который мы можем повторно комбинировать с командами, чтобы run_commands() возвращал список пар (команда, выход):

def run_commands(commands, n_parallel=2):
    worker = multiprocessing.Pool(n_parallel)
    for cmd, output in zip(commands, worker.map(run_command, commands)):
        print("{}: {}".format(cmd, output))

Теперь, поскольку мы печатаем вывод после выполнения всех параллельных процессов, он не перепутается.

1
digitalarbeiter 5 Ноя 2018 в 21:32