2012-03-04 11 views
8

Estoy usando Python como lenguaje de script para hacer un poco de procesamiento de datos y llamar a las herramientas de línea de comandos para el cálculo de números. Deseo ejecutar herramientas de línea de comandos en paralelo ya que son independientes entre sí. Cuando una herramienta de línea de comandos finaliza, puedo recopilar sus resultados del archivo de salida. Así que también necesito un mecanismo de sincronización para notificar a mi programa principal de Python que una tarea ha finalizado para que el resultado pueda ser analizado en mi programa principal.Python, ejecuta herramientas de línea de comandos en paralelo

Actualmente, uso os.system(), que funciona bien para un hilo, pero no se puede paralelizar.

Gracias!

+0

Python tiene un buen soporte para [ 'threading'] (http://docs.python.org/library/threading.html) y [' multiprocessing'] (http://docs.python.org/ library/multiprocessing.html). ¿Cuál es el problema específico que tienes con esos? –

Respuesta

9

Utilice el objeto Pool del módulo multiprocessing. Luego puede usar, p. Pool.map() para hacer un procesamiento paralelo. Un ejemplo sería mi script markphotos (ver a continuación), donde una función se llama varias veces en paralelo a cada proceso de una imagen.

#! /usr/bin/env python 
# -*- coding: utf-8 -*- 
# Adds my copyright notice to photos. 
# 
# Author: R.F. Smith <[email protected]> 
# $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 <[email protected]> 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) 
+0

¡Gracias! y parece que en cada proceso, puedo usar 'os.system' para llamar a mi línea de comandos? –

+1

Mejor uso 'subproceso' para eso. Ver el ejemplo vinculado. –

+0

@RolandSmith - ¿podría restaurar el enlace para marcar las fotos o agregar la fuente aquí? – keflavich

7

Si desea ejecutar herramientas de línea de comandos como procesos separados, simplemente use os.system (o mejor: el módulo subprocess) para iniciarlos de forma asíncrona. En UNIX/Linux/MacOS:

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

En Windows:

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

En cuanto a saber cuando un comando ha terminado: En Unix que podría ponerse en marcha con wait etc., pero si usted está escribiendo los scripts de la línea de comandos, simplemente les pido que escriban un mensaje en un archivo y supervisen el archivo desde el script python de la llamada.

@James Youngman propuso una solución a su segunda pregunta: Sincronización. Si desea controlar sus procesos desde python, puede iniciarlos de forma asincrónica con Popen.

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

Tenga en cuenta que si utiliza Popen y sus procesos de escribir una gran cantidad de datos a la salida estándar, el programa será un punto muerto. Asegúrese de redirigir todo el resultado a un archivo de registro.

p1 y p2 son objetos que puede utilizar para vigilar sus procesos. p1.poll() no bloqueará, pero devolverá None si el proceso aún se está ejecutando. Devolverá el estado de salida cuando esté listo, para que pueda verificar si es cero.

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

Lo anterior es sólo un modelo: Como está escrito, nunca se cerrará, y que se mantendrá "cosecha" los resultados de los procesos terminados. Pero confío en que entiendas la idea.

+1

La prueba sería más general y concisa como 'if any (p.wait() para p en [p1, p2]): ...'. – EOL

+0

Chicos, el OP quiere ejecutar cálculos de forma asincrónica y cosechar los resultados. Él quiere una manera de "notificar a mi programa principal de Python que una tarea ha finalizado". wait() bloqueará hasta que todos los niños hayan terminado. – alexis

+0

@James Youngman: Perdón por revertir los cambios. No esperaba que desaparecieran de la historia. Los incluí en la respuesta (como pueden ver, es una solución separada con problemas separados). – alexis

Cuestiones relacionadas