使用'多处理的最佳实践'在python中打包

时间:2014-03-23 19:07:35

标签: python multiprocessing

我正在multiprocessing尝试使用python模块。我有以下示例代码,它在ipython笔记本中没有任何错误地执行。但我发现在后台生成了额外的python进程,每次执行代码块都在笔记本中。

import multiprocessing as mp

def f(x):
    print "Hello World ", mp.current_process()
    return 1

pool = mp.Pool(3)

data = range(0,10)
pool.map(f, data)

然而,当我在正常的.py文件中保存并执行时,我遇到错误并且必须终止终端以停止执行程序。

我已经通过if __name__ == '__main__':和在此下创建了池并使用pool.close()关闭池来纠正了这个问题。

我很想知道在使用multiprocessing以及mapapplyapply_async等关联功能时应遵循哪些最佳做法?我打算使用这个模块并行读取文件,希望将它应用于少数ML算法,以加快这个过程。

4 个答案:

答案 0 :(得分:4)

你必须把它放在if __name__中的原因是因为当python产生一个新进程时,它会有效地导入这个模块 - 因此试图再次运行不在if __name__块中的任何代码再一次。

最佳做法是将事物保存在合理命名的小型可测试功能中。有一个' main()'函数,然后从if __name__块调用。

避免全局状态(和模块级变量)。它只是让事情变得复杂。相反,想一想将流程传递给流程或从流程传递。这可能很慢,因此首先考虑如何尽可能少地发送数据是有用的。例如,如果您有一个大的配置对象,而不是将整个配置对象发送到每个进程,请将您的流程函数拆分为只需要实际使用的一个或两个属性,然后发送它们。

当事情连续发生时,测试事情会容易得多,因此以这样的方式编写事物,使其顺序发生,而不是使用map或其他任何可以实现的事情更容易。

分析事物是一个好主意,因为整个产生新进程有时最终会比在一个线程中完成所有操作更慢。 gevent模块也非常酷 - 如果你的程序是网络绑定的,那么gevent有时可以比使用多处理更快地并行处理。

答案 1 :(得分:2)

提到的python文档很好 - 请查看Using Python's multiprocessing.Process class。这个问题有一些类似的想法。我还建议您查看https://www.ibm.com/developerworks/aix/library/au-multiprocessing/。它是在python中,并突出了一些很好的pythonic方法来进行多处理。

答案 2 :(得分:1)

官方Python文档有很多用法示例。这可能是学习最佳实践的最佳方式:http://docs.python.org/2/library/multiprocessing.html

答案 3 :(得分:0)

概述,体系结构和一些实用技巧

根据我自己的经验(也是有限的),我可以就多处理如何工作以及如何使用它分享以下见解。我没有发现python.org手册的描述性或图形性很好,因此我阅读了代码。对于每个有相同印象的人……到目前为止,这是我可以弥补的:

一般的最佳/最佳实践提示

  • 一般实施方法:
    • 测试驱动,数据量减少:您不想在几分钟内想知道是否崩溃或计算
    • 分步进行并进行时间分析:
      • 首先,无需多处理即可实现和调试
      • 下一步,实现并调试单进程,分析时间并比较开销,而无需多个进程
      • 接下来,增加进程号和配置文件时间,以识别任何GIL问题和等待时间。
  • 简单的Process或它们的列表对于以少数功能一对一运行功能2进程为目标很有用。
  • Pool处理一组Process es(进程池)之间的可分批工作负载(高级任务/命令)的分配。
  • Pool用于处理器绑定(具有可批量输入/输出的高处理器负载)和pool.ThreadPool用于IO绑定(具有单独的输入/输出的低处理器负载)任务。
  • 对于ProcessPoolThreadThreadPool之间的数据传输,请使用queues.Queue和子类(如果结果顺序很重要)或{ {1}}与Pipe到进程或线程的一对一映射。
  • 共享不同类型(PipeConnectionBaseProxyNamespaceQueue s的变量,或用于设置Pool / {{不同进程之间的1}} / Barrier / Lock / RLock s使用Sempaphore类。
  • 如果无法避免Condition,请使用Manager处理它们,并尝试将密集的计算过程与与GIL相关的计算分开(例如,解析复杂的数据结构,等),然后与Manager或共享的GIL连接。
  • 使用多个Pipe可以将不同数量的进程分配给不同的任务。否则,只需实现一个具有多个映射的Queue或应用方法调用即可。
  • 可以使用单个Pool和多个PoolPool()来计算基于彼此中间结果的顺序并行计算任务。为了使任务彼此同步,映射功能及其方法Pool.(star)map_async()返回的Pool.(star)map()实例是正确的选择。

架构和流程

  • 运行ApplyResult()时,ApplyResult().ready()/.wait()/.get()/.successful()被初始化,它是import multiprocessing的子类,但没有_current_process = MainProcess()BaseProcesstarget,{ {1}},基本上是已经运行的python内核中所有其他args的句柄对象,这些内核导入了kwargs
  • _paraent_pid是Pool的类似API,可能也共享相似的体系结构
  • Process基于3个守护进程线程multiprocessingpool.ThreadPoolPool,它们与1个内部Pool._task_handler Pool._worker_handler和2个内部{ {1}}个Pool._result_handlerqueue.Queue()
  • Pool._taskqueue是一个字典,其中包含所有SimpleQueue和子方法调用中的Pool._inqueue和子类实例,其中Pool._outqueue中的全局Pool._cache为{{1} }。
  • ApplyResults中找到了{li1的
  • Pool.appy_async()/_map_async()ApplyResults._job的子类,并返回了job_counter()和子方法。
  • keyApplyResult之间的区别在于,Pool强制/锁定主进程,以等待所有结果被计算并存储在返回对象Pool._cache中。
  • Pool.apply_async()/._map_async() / Pool.map()游泳池`:
    • Pool.map_async():传送Pool.map() == Pool.map_async().get() / etc的高级作业。从应用方法到ApplyResult()切成任务批次。
    • Queue:以批量方式将作业传递给迭代器?从SimpleQueues inPool.taskqueue
    • Pool.apply_async()/.map_async():将结果从Pool._task_handler(由Pool._inqueue初始化)传送到Pool._task_handlerPool._pool.Process(target=worker, ...)再次将它们放入{{1 }}缓存在Pool._outqueue中。
  • 如果目标Pool._pool.Process(target=worker, ...)有返回对象,则
  • Pool._worker_handler将结果保存为列表。否则Pool._result_handler只是同步方法(即结果状态调用方法)的句柄。
  • 对于连接进程和线程,按从高到低的功能按以下顺序提供了4类:_set()ApplyResultPool._cache[self._job]ApplyResult / {{1} }。 func只是一个返回实际ApplyResult()类实例中的2个实例的方法。

一些代码示例

queues.JoinableQueue