以并行方式运行命令行应用程序

时间:2018-04-10 11:07:30

标签: python windows python-3.x console-application

我正在尝试通过运行从我的Python应用程序启动的第三方可执行应用程序来实现简单的并行数据处理,并且遇到了一些连接并行执行的有趣问题。

用例非常简单。我有一个数据对象列表,应该通过第三方.exe文件处理。我们称之为consoleApp.exe。 应该通过调用此consoleApp来处理每个数据对象几次。 出于测试目的,此控制台应用程序只是将一些文本写入控制台,等待一秒然后退出。

这是我的Python代码,它将进行此类处理

def ProcessFile(idx, row):
    config = UtilitySettings.ConfigFile + " some_arguments"
    config2 = UtilitySettings.config2File + " some_arguments_2"
    config3 = UtilitySettings.config3File + " some_arguments_3"
    config4 = UtilitySettings.config4File + " some_arguments_4"

    fullFileName = AppSettings.BinaryDataDirectory + row.FileName

    cmd1 = "ConsoleApp.exe" + ' ' + fullFileName + ' ' + config
    cmd2 = "ConsoleApp.exe" + ' ' + fullFileName + ' ' +  config2
    cmd3 = "ConsoleApp.exe" + ' ' + fullFileName + ' ' +  config3
    cmd4 = "ConsoleApp.exe" + ' ' + fullFileName + ' ' +  config4

    devnull = open(os.devnull, 'w')
    call(cmd1, stdout=devnull, stderr=devnull)
    call(cmd2, stdout=devnull, stderr=devnull)
    call(cmd3, stdout=devnull, stderr=devnull)

下一代代码将启动并行处理:

class ConvertDataCommand(ICommand):
    def Execute(self):
        startExecutionTime = time.time()
        result = CommandExecutionResult()

        try:
            geoDataFrame = geopandas.GeoDataFrame(objectInfo, crs=crs, geometry = objectInfo.geometry)

            # let's use cpu count - 1 . The last one core will be used by the currently executed parent thread
            coreCount = multiprocessing.cpu_count() - 1
            dataFrameLength = len(geoDataFrame)

            # splitting dataFrame to the chuncks to perform parallel processing
            chunksCount = dataFrameLength / coreCount if dataFrameLength % coreCount == 0 else dataFrameLength / coreCount + 1
            chunkedArr = np.array_split(geoDataFrame, chunksCount)

            for slice in chunkedArr:
                pool = multiprocessing.Pool(processes=coreCount)
                results = [pool.apply(ProcessFile, args=(idx, row)) for idx, row in slice.iterrows()]
                pool.close()
                pool.join()

            result.Success = True

        except BaseException as e:
            result.Success = False
            stacktrace = traceback.format_exc()
            Logger.Log(stacktrace)

        finally:
            result.ExecutionTime = time.time() - startExecutionTime
            return result

主要有趣的是,在我的情况下,并行处理比按顺序处理(142秒)处理此数据需要花费更多时间(178秒)(但它不应该)。 看起来所有核心都使用了这个控制台应用程序(但我希望每个进程都会调用一个新的consoleApp.exe实例并在每个进程中执行它) 我在更改consoleApp.exe的实现时找到了它。 我只是编写一个无限循环并再次启动Python程序。 然后打开Process explorer,它只显示ConsoleApp.exe的一个实例和我的Python应用程序创建的3个进程。

有人知道我做错了什么吗?

1 个答案:

答案 0 :(得分:0)

哦,太好了。 通过调用pool.map()函数而不是pool.apply()解决了这个问题(因为它会阻塞线程,直到结果准备好。有关详细信息,请参阅python-docs

并从以下位置更改调用方法的签名:

def ProcessFile(idx, row):

到:

def ProcessFile(args):
    idx, row = args

for slice in chunkedArr:
    pool = multiprocessing.Pool(processes=coresNum)

    data = [(idx, row) for idx, row in slice.iterrows()]
    results = pool.map(ProcessFile, data)

    pool.close()
    pool.join()