使用Python多进程天真的话会遇到什么问题?
我们正在考虑对一个大型应用程序进行重构,这个应用程序有一个复杂的图形用户界面(GUI),并且它与后端是解耦的。我们想使用新的(Python 2.6)多进程模块。这个GUI和后端之间的接口使用队列(Queues),并且在两个方向上交换消息对象。
我刚刚得出的一个结论(虽然还不太确定,但欢迎大家确认)是,“对象身份”在多进程接口中不会被保留。目前,当我们的GUI向后端发送一条消息时,它希望能收到同样的消息,并且附带一个结果作为属性。它使用对象身份(if received_msg is message_i_sent:
)来识别返回的消息,但这在多进程中可能行不通。
这个问题是想请教大家在实际使用中遇到过哪些类似的“坑”,或者你能想象在简单使用多进程模块时会遇到哪些问题,特别是在重构一个已有的单进程应用时。请说明你的回答是否基于实际经验。如果能提供一个可行的解决方法,那就更好了。
编辑:虽然我提这个问题的目的是想收集一些关于问题的描述,但我觉得我犯了两个错误:我一开始就把它设为社区维基(这可能让很多人忽视它,因为他们得不到声望点),而且我提供了一个太具体的例子——虽然我很感激大家的回答,但这可能让很多人错过了我对一般性回应的请求。我可能会重新措辞并在新问题中再次提问。现在我只是接受一个答案作为最佳答案,以便结束这个问题,特别是关于我提供的具体例子。感谢那些回答的人!
3 个回答
你可以试试我项目 GarlicSim 里的 persistent
包。它是 LGPL 授权的。
http://github.com/cool-RR/GarlicSim/tree/development/garlicsim/garlicsim/misc/persistent/
里面的主要模块是 persistent.py
。
我经常这样使用它:
# ...
self.identity = Persistent()
这样我就可以在不同的进程之间保持一个身份。
当然,检查非单例对象的身份(比如说“a 是 None”或者“a 是 False”)通常不是一个好习惯——虽然这样做可能很快,但一个更简单的解决办法是把“is”换成“==”来进行比较,并使用一个递增的计数器来定义身份:
# 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)
这可能是个不错的主意。
另外,要注意,如果你有很多“工作进程”,那么内存的消耗可能会比基于线程的方法要大得多。
我自己没有直接使用过多进程,但遇到的问题和我在其他两个领域的经验很相似:分布式系统和对象数据库。Python中的对象身份既有好处也有坏处!
关于一些常见的问题,如果你正在重构的应用程序能够意识到任务是异步处理的,那就会更好。如果不能,你通常会需要管理锁,这样一来,你本来可以通过使用独立进程获得的性能提升,就会因为等待这些锁而损失掉。我还建议你花时间为跨进程调试建立一些基础设施。真正的异步进程往往会做很多事情,超出我们大脑能理解和验证的范围——至少我自己是这样!
针对具体情况,我会在进程边界管理对象身份,也就是在任务排队和返回时。当发送一个任务去处理时,给这个任务加上一个id(),并把任务实例存放在一个字典里,用id()作为键。当任务更新或完成时,从字典中通过id()取回这个具体的任务,并把新更新的状态应用到它上面。这样,具体的任务及其身份就能得到保持。