2012-03-04 45 views
8

Tôi đang sử dụng Python làm ngôn ngữ kịch bản để thực hiện một số công cụ xử lý dữ liệu và gọi các công cụ dòng lệnh cho việc crunching số. Tôi muốn chạy các công cụ dòng lệnh song song vì chúng độc lập với nhau. Khi một công cụ dòng lệnh kết thúc, tôi có thể thu thập kết quả của nó từ tệp đầu ra. Vì vậy, tôi cũng cần một số cơ chế đồng bộ hóa để thông báo cho chương trình Python chính của tôi rằng một nhiệm vụ được hoàn thành để kết quả có thể được phân tích cú pháp vào chương trình chính của tôi.Python, chạy các công cụ dòng lệnh song song

Hiện tại, tôi sử dụng os.system(), hoạt động tốt cho một chuỗi nhưng không thể song song.

Cảm ơn!

+0

Python có hỗ trợ tốt cho ['threading'] (http://docs.python.org/library/threading.html) và [' multiprocessing'] (http://docs.python.org/ library/multiprocessing.html). Vấn đề cụ thể mà bạn có với những vấn đề này là gì? –

Trả lời

9

Sử dụng đối tượng Pool từ mô-đun multiprocessing. Sau đó, bạn có thể sử dụng ví dụ: Pool.map() để thực hiện xử lý song song. Một ví dụ sẽ là kịch bản markphotos của tôi (xem bên dưới), trong đó một hàm được gọi nhiều lần song song với mỗi quá trình một hình ảnh.

#! /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

Cảm ơn! và có vẻ như ở mỗi quá trình, tôi có thể sử dụng 'os.system' để gọi dòng lệnh của mình? –

+1

Sử dụng tốt hơn 'tiến trình con' cho điều đó. Xem ví dụ được liên kết. –

+0

@RolandSmith - bạn có thể khôi phục liên kết tới hình ảnh đánh dấu hoặc thêm nguồn ở đây không? – keflavich

7

Nếu bạn muốn chạy các công cụ dòng lệnh dưới dạng các quy trình riêng biệt, chỉ cần sử dụng os.system (hoặc tốt hơn: mô-đun subprocess) để bắt đầu chúng không đồng bộ. Trên Unix/Linux/macos:

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

Trên Windows:

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

Đối biết khi nào một lệnh đã hoàn tất: Theo unix bạn có thể có được thiết lập với wait vv, nhưng nếu bạn viết kịch bản lệnh, tôi chỉ muốn họ viết một tin nhắn vào một tập tin, và theo dõi tập tin từ tập lệnh python đang gọi.

@James Youngman đề xuất giải pháp cho câu hỏi thứ hai của bạn: Đồng bộ hóa. Nếu bạn muốn kiểm soát các quá trình của bạn từ python, bạn có thể bắt đầu chúng không đồng bộ với Popen.

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

Hãy cẩn thận nếu bạn sử dụng Popen và các quy trình của bạn ghi nhiều dữ liệu để xuất, chương trình của bạn sẽ bế tắc. Đảm bảo chuyển hướng tất cả đầu ra đến tệp nhật ký.

p1p2 là các đối tượng mà bạn có thể sử dụng để giữ các tab trên các quy trình của mình. p1.poll() sẽ không chặn, nhưng sẽ trả về Không nếu quá trình này vẫn đang chạy. Nó sẽ trả về trạng thái thoát khi nó được thực hiện, vì vậy bạn có thể kiểm tra nếu nó là số không.

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 

Ở trên chỉ là một mô hình: Như được viết, nó sẽ không bao giờ thoát và sẽ giữ "thu hoạch" kết quả của quá trình hoàn tất. Nhưng tôi tin tưởng bạn có được ý tưởng.

+1

Bài kiểm tra sẽ tổng quát hơn và ngắn gọn như 'nếu có (p.wait() cho p trong [p1, p2]):…'. – EOL

+0

Guys, OP muốn chạy tính toán không đồng bộ và thu thập kết quả. Anh ấy muốn một cách "để thông báo cho chương trình Python chính của tôi rằng một nhiệm vụ đã hoàn thành". wait() sẽ chặn cho đến khi tất cả trẻ em kết thúc. – alexis

+0

@James Youngman: Xin lỗi vì đã quay lại các thay đổi của bạn. Tôi không mong đợi họ biến mất khỏi lịch sử. Tôi đưa chúng vào câu trả lời (như bạn có thể thấy, đó là một giải pháp riêng biệt với các vấn đề riêng biệt). – alexis

Các vấn đề liên quan