如何实时共享python进程之间的对象和数据?

时间:2014-10-24 18:58:13

标签: python multiprocessing

我正在尝试在Python中为实时应用程序,多处理和大文件找到合理的方法。

父进程生成2个或更多子进程。第一个孩子读取数据,保留在内存中,其他孩子以流水线方式处理数据。数据应组织成一个对象,发送到以下过程,处理,发送,处理等等。

由于开销(序列化等),管道,队列,管理器等可用方法似乎不够用。

有足够的方法吗?

5 个答案:

答案 0 :(得分:2)

我已经在高内存应用程序中使用Celery和Redis进行实时多处理,但这实际上取决于您要完成的任务。

我在Celery中发现的内置多处理工具(管道/队列)的最大好处是:

  • 低开销。您可以直接调用函数,无需序列化数据。
  • 缩放。需要加强工作流程吗?只需添加更多工人。
  • 透明度。易于检查任务/工作人员并找到瓶颈。

为了真正挤出性能,ZMQ是我的目标。设置和微调的工作要多得多,但它可以安全地接近裸插座。

免责声明:这都是轶事。这实际上取决于您的具体需求。在你走下任何道路之前,我会使用样本数据对不同的选项进行基准测试。

答案 1 :(得分:2)

首先,怀疑由于所有开销导致的消息传递可能不充分,这并不是使程序过度复杂化的好理由。建立概念验证并提出一些样本数据并开始测试是一个很好的理由。如果你花80%的时间腌制东西或通过队列推送东西,那么是的,这可能会成为你现实代码中的一个问题 - 假设你的概念验证的工作量很大与您的真实代码相当。但如果你花98%的时间做真正的工作,那么就没有问题需要解决。消息传递将更简单,所以只需使用它。

此外,即使你确实在这里发现问题,也并不意味着你必须放弃传递信息;它可能只是multiprocessing内置的问题。像0MQ和Celery这样的技术可能比简单的队列具有更低的开销。即使对通过队列发送的内容更加谨慎也会产生巨大的差异。


但是如果消息传递结束,显而易见的替代方案是数据共享。这在multiprocessing文档中得到了很好的解释,以及每个文档的优缺点。

Sharing state between processes介绍了如何操作的基础知识。还有其他替代方法,例如使用特定于平台的共享内存API的mmap ped文件,但除了您需要(例如,运行之间的持久存储)之外,在multiprocessing上没有太多理由这样做

有两个大问题要处理,但两者都可以处理。

首先,您不能共享Python对象,只能共享简单的值。 Python对象在整个地方都有相互内部引用,垃圾收集器无法看到其他进程中对象的引用'堆,等等。因此,multiprocessing.Value只能保存与array.array相同的基本类型的原生值,而multiprocessing.Array可以保存(正如您在名称中所猜测的)具有相同值的1D数组,就是这样。对于任何更复杂的事情,如果您可以使用ctypes.Structure来定义它,则可以使用https://docs.python.org/3/library/multiprocessing.html#module-multiprocessing.sharedctypes,但这仍然意味着对象之间的任何引用都必须是间接的。 (例如,您经常需要将索引存储到数组中。)(当然,如果您使用NumPy,这一切都不是坏消息,因为您可能已经将大部分数据存储在NumPy阵列中简单的值,可以共享。)

其次,共享数据当然受竞争条件的影响。并且,与单个流程中的多线程不同,您不能依靠GIL来帮助保护您;有多个解释器都可以尝试同时修改相同的数据。所以你必须使用锁或条件来保护事物。

答案 2 :(得分:0)

对于多处理管道,请查看MPipe

对于共享内存(特别是NumPy数组),请查看numpy-sharedmem

我使用它们来执行高性能实时,并行图像处理(使用OpenCV进行平均累积和面部检测),同时从多核CPU系统中挤出所有可用资源。如果有兴趣,请查看Sherlock。希望这会有所帮助。

答案 3 :(得分:0)

一种选择是使用类似brain-plasma这样的东西来维护一个独立于Python进程或线程的共享内存对象名称空间。有点像Redis,但是可以与大对象一起使用,并且具有基于Apache Arrow的简单API。

$ pip install brain-plasma
# process 1
from brain_plasma import Brain
brain = Brain()
brain['myvar'] = 657
# process 2
from brain_plasma import Brain
brain = Brain()
brain['myvar']
# >>> 657

答案 4 :(得分:0)

Python 3.8现在使用multiprocessing.shared_memory在进程之间提供共享内存访问。您在进程之间传递的只是一个引用共享内存块的字符串。在使用过程中,您将获得一个memoryview对象,该对象支持切片,而无需像字节数组那样复制数据。如果使用的是numpy,则可以在O(1)操作中引用该内存块,从而可以快速传输大型数字数据块。据我了解,通用对象仍然需要反序列化,因为原始字节数组是使用过程所接收的。