2010-11-26 31 views
3

我在運行Python 2.6的Linux機器上並行運行多個命令,這可能需要一些時間。更好的多線程使用Python subprocess.Popen&communications()?

因此,我使用subprocess.Popen類和process.communicate()方法來並行執行多個命令組並在執行後立即捕獲輸出。

def run_commands(commands, print_lock): 
    # this part runs in parallel. 
    outputs = [] 
    for command in commands: 
     proc = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True) 
     output, unused_err = proc.communicate() # buffers the output 
     retcode = proc.poll()     # ensures subprocess termination 
     outputs.append(output) 
    with print_lock: # print them at once (synchronized) 
     for output in outputs: 
      for line in output.splitlines(): 
       print(line) 

在別的地方,它被稱爲是這樣的:

processes = [] 
print_lock = Lock() 
for ...: 
    commands = ... # a group of commands is generated, which takes some time. 
    processes.append(Thread(target=run_commands, args=(commands, print_lock))) 
    processes[-1].start() 
for p in processes: p.join() 
print('done.') 

預期的結果是一組命令的每個輸出在一次,而他們的執行並行完成顯示。

但是從第二個輸出組(當然,由於調度不確定性而變成第二個線程),它開始打印而沒有換行符,並且添加的空格數與前一行中打印的字符數和輸入回顯關閉 - 終端狀態爲「亂碼」或「崩潰」。 (如果我發出reset shell命令,它恢復正常。)

起初,我試圖找到處理'\r'的原因,但它不是原因。正如您在我的代碼中看到的那樣,我使用splitlines()正確處理了它,並且我確認了使用repr()函數應用於輸出。

我認爲原因是在Popencommunicate()管道stdout/stderr併發使用管道。我在Python 2.7中嘗試了check_output快捷方法,但沒有成功。當然,如果我序列化所有命令的執行和打印,則不會發生上述問題。

有沒有更好的方法可以並行處理Popencommunicate()

+1

`.communicate()`確保進程完成,因爲它調用`Popen.wait()`。但`proc.poll()`不能確保進程完成。如果進程沒有完成,它將返回`None`。例如在Linux上,它調用`waitpid(pid,WNOHANG)`。 WNOHANG的文檔說:「如果狀態不是立即可用於由pid指定的某個子進程,則waitpid()函數不會暫停執行調用線程。」 http://www.mkssoftware.com/docs/man3/waitpid.3。asp – jfs 2010-11-26 19:57:08

+1

以下是腳本,它並行運行所有進程,並按進程組輸出組,以保持組內的順序https://gist.github.com/717467 – jfs 2010-11-27 01:50:45

+0

感謝Sebastian,但是您的解決方案並未完全解決問題。這似乎是Python的子流程實現及其同步中的一個錯誤。我添加了最終結果作爲答案。 – Achimnol 2010-11-28 20:15:43

回答

0

我不確定它清楚run_commands需要實際做什麼,但它似乎只是在子流程上進行輪詢,忽略返回碼並繼續循環。當你到達你正在打印輸出的部分時,你怎麼知道子流程已經完成?

0

在你的示例代碼中,我注意到您的使用:

for line in output.splitlines(): 

,以解決部分的 「/R」 的問題;使用

for line in output.splitlines(True): 

本來是有幫助的。