【问题标题】:Python: execute cat subprocess in parallelPython:并行执行 cat 子进程
【发布时间】:2014-06-29 22:55:27
【问题描述】:

我正在远程服务器上运行多个cat | zgrep 命令并分别收集它们的输出以进行进一步处理:

class MainProcessor(mp.Process):
    def __init__(self, peaks_array):
        super(MainProcessor, self).__init__()
        self.peaks_array = peaks_array

    def run(self):
        for peak_arr in self.peaks_array:
            peak_processor = PeakProcessor(peak_arr)
            peak_processor.start()

class PeakProcessor(mp.Process):
    def __init__(self, peak_arr):
        super(PeakProcessor, self).__init__()
        self.peak_arr = peak_arr

    def run(self):
        command = 'ssh remote_host cat files_to_process | zgrep --mmap "regex" '
        log_lines = (subprocess.check_output(command, shell=True)).split('\n')
        process_data(log_lines)

然而,这会导致 subprocess('ssh ... cat ...') 命令的顺序执行。第二个高峰等待第一个完成,依此类推。

如何修改此代码以使子进程调用并行运行,同时仍能单独收集每个子进程的输出?

【问题讨论】:

  • --mmap 从管道读取时没用...

标签: python shell subprocess python-multithreading


【解决方案1】:

您不需要multiprocessingthreading 来并行运行子进程。例如:

#!/usr/bin/env python
from subprocess import Popen

# run commands in parallel
processes = [Popen("echo {i:d}; sleep 2; echo {i:d}".format(i=i), shell=True)
             for i in range(5)]
# collect statuses
exitcodes = [p.wait() for p in processes]

它同时运行 5 个 shell 命令。注意:这里既不使用线程也不使用multiprocessing 模块。在 shell 命令中添加与符号 & 是没有意义的:Popen 不会等待命令完成。您需要显式调用.wait()

很方便,但不需要使用线程来收集子进程的输出:

#!/usr/bin/env python
from multiprocessing.dummy import Pool # thread pool
from subprocess import Popen, PIPE, STDOUT

# run commands in parallel
processes = [Popen("echo {i:d}; sleep 2; echo {i:d}".format(i=i), shell=True,
                   stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)
             for i in range(5)]

# collect output in parallel
def get_lines(process):
    return process.communicate()[0].splitlines()

outputs = Pool(len(processes)).map(get_lines, processes)

相关:Python threading multiple bash subprocesses?.

以下代码示例在同一线程中同时从多个子进程获取输出:

#!/usr/bin/env python3
import asyncio
import sys
from asyncio.subprocess import PIPE, STDOUT

@asyncio.coroutine
def get_lines(shell_command):
    p = yield from asyncio.create_subprocess_shell(shell_command,
            stdin=PIPE, stdout=PIPE, stderr=STDOUT)
    return (yield from p.communicate())[0].splitlines()

if sys.platform.startswith('win'):
    loop = asyncio.ProactorEventLoop() # for subprocess' pipes on Windows
    asyncio.set_event_loop(loop)
else:
    loop = asyncio.get_event_loop()

# get commands output in parallel
coros = [get_lines('"{e}" -c "print({i:d}); import time; time.sleep({i:d})"'
                    .format(i=i, e=sys.executable)) for i in range(5)]
print(loop.run_until_complete(asyncio.gather(*coros)))
loop.close()

【讨论】:

  • @j-f-sebastian 嗯...我对您的答案中代码 sn-ps #2 和 #3 之间的区别感到困惑。您能否指出一些资源或解释“获取输出...在同一个线程中”是什么意思?顺便说一句,非常感谢#2 :)
  • @SaheelGodhane:基于multiprocessing.dummy.Pool() 的解决方案使用多个(多个/多个)线程。 asyncio-based 解决方案在这里使用 single 线程。要了解如何在同一个线程中同时执行多项操作,请参阅Python Concurrency From the Ground Up: LIVE!
  • 很好的例子!我尝试使用新的 subprocess.run() 功能实现 sn-ps #1,但看起来这不起作用,因为该功能始终等待进程完成。我不得不切换回使用 Popen。
  • @jfs 你太棒了!我一直在寻找一种在 Windows 上并行执行命令而不使用 if __name__ == __main__ 的方法:两天来,这是我需要的突破,我最终专门为它做了一个问答,因为我很难弄清楚在查看了 20 多个类似的问答之后,了解如何做到这一点。 stackoverflow.com/questions/53983147/…
【解决方案2】:

另一种方法(而不是其他将 shell 进程置于后台的建议)是使用multithreading.

您拥有的 run 方法会执行以下操作:

thread.start_new_thread ( myFuncThatDoesZGrep)

要收集结果,您可以执行以下操作:

class MyThread(threading.Thread):
   def run(self):
       self.finished = False
       # Your code to run the command here.
       blahBlah()
       # When finished....
       self.finished = True
       self.results = []

按照上面关于多线程的链接中的说明运行线程。当您的线程对象具有 myThread.finished == True 时,您可以通过 myThread.results 收集结果。

【讨论】:

  • 使用这种方法,一旦线程完成运行,我如何获得每个的输出?而且我已经在使用一个进程,为什么一个线程可以工作而不是一个进程?
  • 一个进程将起作用 - 另一个陈述的答案建议您通过使用 & 在实际的 shell 中执行多进程工作。在这种方法中,您只有一个 python 进程,但它会产生许多 shell 进程。在多线程方法中,您有多个 python 进程,但每个 python 进程有一个 shell 进程。要从多个线程收集结果,您需要创建 Thread 子类的类。然后将一个线程的结果作为对象数据放入该类中。
  • 但是上面的代码不就是这样吗?我正在为每个峰值启动一个新进程,然后从它的 run 方法运行 subprocess 和 process_data。
  • 不,当您运行子进程时,您的代码会阻塞(停止运行),直到命令完成。因此,每次执行 run() 时,您都在串行运行每个命令。如果你想并行执行,这就是线程的用武之地——你并行运行多个线程,每个线程串行运行一个命令。
  • 太棒了,self.finished 变量真的有必要吗?还是调用thread.join() 就足够了?
猜你喜欢
  • 2012-04-02
  • 1970-01-01
  • 1970-01-01
  • 2020-06-16
  • 2014-08-20
  • 2015-03-31
  • 1970-01-01
  • 2013-05-03
  • 2012-10-15
相关资源
最近更新 更多