Python, параллельно запускающий инструменты командной строки - PullRequest
11 голосов
/ 04 марта 2012

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

В настоящее время я использую os.system(), который отлично работает для однопоточности, но не может быть распараллелен.

Спасибо!

Ответы [ 2 ]

9 голосов
/ 04 марта 2012

Используйте объект Pool из модуля multiprocessing. Затем вы можете использовать, например, Pool.map() для параллельной обработки. Примером может служить мой скрипт markphotos (см. Ниже), в котором функция вызывается несколько раз параллельно каждому процессу изображения.

#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Adds my copyright notice to photos.
#
# Author: R.F. Smith <rsmith@xs4all.nl>
# $Date: 2012-10-28 17:00:24 +0100 $
#
# To the extent possible under law, Roland Smith has waived all copyright and
# related or neighboring rights to markphotos.py. This work is published from
# the Netherlands. See http://creativecommons.org/publicdomain/zero/1.0/

import sys
import subprocess
from multiprocessing import Pool, Lock
from os import utime, devnull
import os.path
from time import mktime

globallock = Lock() 

def processfile(name):
    """Adds copyright notice to the file.

    Arguments:
    name -- file to modify
    """
    args = ['exiftool', '-CreateDate', name]
    createdate = subprocess.check_output(args)
    fields = createdate.split(":") #pylint: disable=E1103
    year = int(fields[1])
    cr = "R.F. Smith <rsmith@xs4all.nl> http://rsmith.home.xs4all.nl/"
    cmt = "Copyright © {} {}".format(year, cr)
    args = ['exiftool', '-Copyright="Copyright (C) {} {}"'.format(year, cr),
            '-Comment="{}"'.format(cmt), '-overwrite_original', '-q', name]
    rv = subprocess.call(args)
    modtime = int(mktime((year, int(fields[2]), int(fields[3][:2]),
                          int(fields[3][3:]), int(fields[4]), int(fields[5]),
                          0,0,-1)))
    utime(name, (modtime, modtime))
    globallock.acquire()
    if rv == 0:
        print "File '{}' processed.".format(name)
    else:
        print "Error when processing file '{}'".format(name)
    globallock.release()

def checkfor(args):
    """Make sure that a program necessary for using this script is
    available.

    Arguments:
    args -- list of commands to pass to subprocess.call.
    """
    if isinstance(args, str):
        args = args.split()
    try:
        with open(devnull, 'w') as f:
            subprocess.call(args, stderr=subprocess.STDOUT, stdout=f)
    except:
        print "Required program '{}' not found! exiting.".format(args[0])
        sys.exit(1)

def main(argv):
    """Main program.

    Arguments:
    argv -- command line arguments
    """
    if len(argv) == 1:
        binary = os.path.basename(argv[0])
        print "Usage: {} [file ...]".format(binary)
        sys.exit(0)
    checkfor(['exiftool',  '-ver'])
    p = Pool()
    p.map(processfile, argv[1:])
    p.close()

if __name__ == '__main__':
    main(sys.argv)
9 голосов
/ 04 марта 2012

Если вы хотите запускать инструменты командной строки как отдельные процессы, просто используйте os.system (или лучше: модуль subprocess), чтобы запустить их асинхронно.В Unix / Linux / Macos:

subprocess.call("command -flags arguments &", shell=True)

В Windows:

subprocess.call("start command -flags arguments", shell=True)

Как узнать, когда команда закончилась: В Unix вы можете настроить с wait и т.д., но если вы пишете сценарии командной строки, я бы просто попросил их записать сообщение в файл и следить за файлом из вызывающего сценария python.

@ Джеймс Янгман предложил решение вашего второго вопросаСинхронизация.Если вы хотите управлять своими процессами из Python, вы можете запустить их асинхронно с помощью Popen.

p1 = subprocess.Popen("command1 -flags arguments")
p2 = subprocess.Popen("command2 -flags arguments")

Помните, что если вы используете Popen и ваши процессы записывают много данных в стандартный вывод, ваша программа заходит в тупик.Обязательно перенаправьте весь вывод в файл журнала.

p1 и p2 - это объекты, которые вы можете использовать для отслеживания своих процессов.p1.poll() не будет блокировать, но вернет None, если процесс все еще выполняется.Когда это будет сделано, он вернет статус выхода, поэтому вы можете проверить, равен ли он нулю.

while True:
    time.sleep(60)
    for proc in [p1, p2]:
        status = proc.poll()
        if status == None:
            continue
        elif status == 0:
            # harvest the answers
        else:
            print "command1 failed with status", status

Выше приведена только модель: как написано, она никогда не выйдет и будет продолжать "сбор«Результаты завершенных процессов.Но я верю, что вы поняли идею.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...