【问题标题】:Execute parallell .py scripts执行并行 .py 脚本
【发布时间】:2019-03-19 04:35:17
【问题描述】:

说我有 scrapper_1.py、scrapper_2.py、scrapper_3.py。

我现在运行它的方式是从 pycharm 单独运行/执行,这样我可以在任务管理器中看到 3 python.exe 正在执行。

现在我正在尝试编写一个主脚本,比如 scrapper_runner.py ,将这个抓取器作为模块导入并并行而不是顺序地运行它们。

我从各种 SO 帖子中尝试了子进程、多处理甚至 os.system 的示例……但没有任何运气……从日志中它们都按顺序运行,从任务管理器中我只看到一个 python.exe 执行。

这种流程的模式正确吗?

EDIT:1(尝试使用 concurrent.futures ProcessPoolExecutor)它按顺序运行。

from concurrent.futures import ProcessPoolExecutor

import scrapers.scraper_1 as scraper_1
import scrapers.scraper_2 as scraper_2
import scrapers.scraper_3 as scraper_3

## Calling method runner on each scrapper_x to kick off processes
runners_list = [scraper_1.runner(), scraper_1.runner(), scraper_3.runner()]



if __name__ == "__main__":


    with ProcessPoolExecutor(max_workers=10) as executor:
        for runner in runners_list:
            future = executor.submit(runner)
            print(future.result())

【问题讨论】:

  • 请展示您的测试代码。多个进程可以在任务管理器中组合在一起,因此请确保您不会在那里错过它们。
  • @JohanL 添加了编辑 1,包括我对 comcurrent.futures 的尝试

标签: python subprocess python-multiprocessing python-multithreading python-module


【解决方案1】:

python 中的子进程可能会或可能不会显示为单独的进程,具体取决于您的操作系统和任务管理器。以linux中的htop为例,会在树状图中显示父进程下的子进程。

我建议看一下关于 python 中 multiprocessing 模块的深入教程:https://pymotw.com/2/multiprocessing/basics.html

但是,如果 python 的内置多处理/线程方法对您不起作用或对您没有意义,您可以通过使用 bash 调用您的 python 脚本来实现您想要的结果。以下 bash 脚本会生成随附的屏幕截图。

#!/bin/sh
./py1.py &
./py2.py &
./py3.py &

解释:每个调用结束时的& 告诉 bash 将每个调用作为后台进程运行。

【讨论】:

  • 好吧,既然 OP 对 Python 代码有完全的控制权,那么将它变成一个单一的 Python 多处理程序是很有可能的。此外,即使没有这个,也可以从一个 Python 调用中运行多个子进程。这是一种非常有效的方式,可以在 Python 中编排流程,而不是直接在 shell 中或作为 shell 脚本。
  • 不确定“这不是一个单独的过程”是什么意思? ... Python 的subprocess 模块用于生成新进程。你的区别是什么?
【解决方案2】:

您的问题在于如何设置流程。即使您认为自己是并行运行的,您也没有并行运行这些进程。当您将它们添加到 runners_list 时,您实际上正在运行它们,然后您将每个运行器的结果作为多进程并行运行。

您想要做的是将函数添加到runners_list 而不执行它们,然后让它们在您的多处理pool 中执行。实现这一点的方法是添加函数引用,即函数的名称。为此,您不应包含括号,因为这是调用函数的语法,而不仅仅是命名函数。

此外,要让期货异步执行,不可能直接调用future.result,因为这将强制代码按顺序执行,以确保结果与函数被调用。

这意味着你的问题的灵魂是

from concurrent.futures import ProcessPoolExecutor

import scrapers.scraper_1 as scraper_1
import scrapers.scraper_2 as scraper_2
import scrapers.scraper_3 as scraper_3

## NOT calling method runner on each scrapper_x to kick off processes
## Instead add them to the list of functions to be run in the pool
runners_list = [scraper_1.runner, scraper_1.runner, scraper_3.runner]

# Adding callback function to call when future is done.
# If result is not printed in callback, the future.result call will
# serialize the call sequence to ensure results in order
def print_result(future):
    print(future.result)

if __name__ == "__main__":
    with ProcessPoolExecutor(max_workers=10) as executor:
        for runner in runners_list:
            future = executor.submit(runner)
            future.add_done_callback(print_result)

如您所见,这里的 runners 调用不会在创建列表时发生,而是在稍后将 runner 提交给执行程序时发生。并且,当结果准备好时,会调用回调,将结果打印到屏幕上。

【讨论】:

  • 不幸的是它仍然按顺序运行:-(
  • 是的,这是由于调用了print(future.result())。让我尽快为您解决这个问题。
  • 它很奇怪......你的解释对我来说都很有意义......但它仍然按顺序工作
  • @JorgeVidinha 好的,这是奇怪,因为这在我的机器上并行工作(尽管使用我自己的简单“Scraper”类)。您如何设置 Scrapers 以及 runner 函数在做什么?它是创建对象后调用的第一个方法吗?您是在为您的 Scraper 创建对象还是它们不是类?
  • - 不,它们不是类。 runner() 方法启动一个 for 循环,该循环调用第二个方法 page_scanner(page=1) 并使用要废弃的页码作为参数。
猜你喜欢
  • 2019-12-16
  • 1970-01-01
  • 1970-01-01
  • 2014-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-05
  • 2018-12-16
相关资源
最近更新 更多