天真地使用Python多处理会遇到什么问题?

问题描述:

我们正在考虑使用与后端分离的复杂GUI来重新构建一个大型应用程序,以使用新的(Python 2.6)多处理模块。 GUI /后端接口使用两个方向交换消息对象的队列。天真地使用Python多处理会遇到什么问题?

我刚刚断定的一件事(暂时,但随时可以确认)是“对象身份”不会保留在多处理接口中。目前,当我们的GUI向后端发布消息时,它期望获得相同的消息并将结果附加为属性。在某些情况下,它使用对象标识(if received_msg is message_i_sent:)来标识返回的消息......并且这看起来可能不适用于多处理。

这个问题是要问什么“陷阱”这样你已在实际使用中看到能想象一个会遇到在天真地使用多模块,尤其是在重构现有的单过程中的应用。请说明您的答案是否基于实际经验。奖励积分为问题提供可用的解决方法。

编辑:虽然我这个问题的意图是一般收集的问题的描述,我觉得我犯了两个错误:我把它从一开始社区维基(这可能让很多人忽略了它,他们不会获得声望点),而且我包含了一个过于具体的例子,虽然我很欣赏答案,但可能会让很多人错过对一般答复的请求。我可能会重新提出一个新问题并重新提出这个问题。就目前而言,我只接受一个答案,仅仅是关于这个问题,只要它与我包含的具体例子相关。感谢那些回答过的人!

+1

'message_i_sent.id = id(message_i_sent)'然后'如果received_msg.id == message_i_sent.id'可以解决身份识别(如果存在)。还有很多其他人。 – jfs 2009-12-18 01:37:47

+0

@JF,谢谢,我想我比我的选择更好,它会使用一个锁定保护的类属性增加一个在Message.__ init__中的id。你的方法似乎不需要锁定或递增或类属性,但仍然是完全健壮的。除非你或其他人先做,否则我会把它写成答案。 – 2009-12-18 01:47:53

+0

@JF,嗯....仍然在思考,但我现在还不太确定。如果先前的ID为1234的消息经过了接口,但是在发送端没有引用,然后另一个消息得到了相同的ID()并且也被发送,这次保留了引用,但是* first *消息然后回来,它会错误地匹配。跟着我? – 2009-12-18 01:50:25

我没有使用多重处理本身,但提出的问题与我在其他两个领域的经验类似:分布式系统和对象数据库。 Python对象身份可以是祝福和诅咒!

至于一般性问题,如果您正在重构的应用程序可以确认任务正在异步处理,则会有所帮助。如果不是这样,你通常最终会管理锁,并且使用单独进程可能获得的许多性能将会因等待这些锁而丢失。我也会建议你花时间在各个流程上构建一些脚手架来进行调试。真正的异步过程往往比心智可以持有和验证更多 - 或者至少是我的想法!

对于概述的具体情况,当物品排队并返回时,我将在流程边界管理对象标识。发送要处理的任务时,使用id()对任务进行注释,并使用id()作为密钥将任务实例存储在字典中。任务更新/完成时,从字典中检索id()返回的确切任务,并将新更新的状态应用于该任务。现在确切的任务,因此它的身份,将保持。

+0

@Shane,不错(对于id()的想法)。由于我们的GUI后端消息相对较少(与In-GUI或后端消息卷相比),状态复制不应构成巨大的负担。如果标记和存储的消息从不返回,则存在内存泄漏的可能性,但这应该是由错误引起的异常情况,而不是常规情况。 – 2009-12-18 13:25:32

+1

@Peter,我已经处理了这个问题,通过给消息条目添加一个超时值 - 无论是用户提供的还是隐式的。如果在回复到达之前超时已到期,请发送错误处理程序。您还将必须处理迟到的回复。 对于状态复制,您可以删除从消息对象发送时不再需要的任何数据,并仍在发送方维护标识属性。如果你的数据不是python对象还有其他的选择。将mmap或memcached用于面向内存的数据,或使用对外部URL,文件和数据库的引用。 – 2009-12-21 19:18:59

那么,当然在非单身物体(例如“a是无”或“a是假”)上测试身份通常不是一个好的做法 - 它可能很快,但是一个非常快速的解决方法将交换“是”为“==”测试和使用的增量计数器来确定身份:

# this is not threadsafe. 
class Message(object): 
    def _next_id(): 
     i = 0 
     while True: 
      i += 1 
      yield i 
    _idgen = _next_id() 
    del _next_id 

    def __init__(self): 
     self.id = self._idgen.next() 

    def __eq__(self, other): 
     return (self.__class__ == other.__class__) and (self.id == other.id) 

这可能是一个想法。

另外,请注意,如果您有大量“工作进程”,内存消耗可能远远大于基于线程的方法。

+0

谢谢艾伦。你所展示的就像我在给J.F.Sebastian的评论中提到的,除了我们需要围绕增量操作进行线程锁定之外。至于工作进程,在我们的例子中,想法是仅分离GUI以确保良好的用户响应并且最小化GUI中用户活动对后端操作的影响。 – 2009-12-18 13:29:30

+0

是的,我甚至对增加的东西不是线程安全做了评论,但我想我在切割和粘贴到网站时切碎了它。 对于您的使用案例,您可以考虑采用异步方法,您是否想过Twisted? – 2009-12-18 13:46:01

+0

后端实际上是多线程和异步的,有多个Reactors。它“仅稍微扭曲”......使用我们称之为“弯曲”的包装。它不能有效地作为纯粹的异步(一个线程),所以我们坚持锁定这样的事情。虽然好想。 – 2009-12-18 15:44:35

您可以尝试我项目GarlicSim中的persistent包。这是LGPL'ed。

http://github.com/cool-RR/GarlicSim/tree/development/garlicsim/garlicsim/misc/persistent/

(它的主要模块是persistent.py

我经常用这样的:

# ... 
self.identity = Persistent() 

然后,我有一个通过流程保留身份。