使用Python多进程天真的话会遇到什么问题?

7 投票
3 回答
1425 浏览
提问于 2025-04-15 17:09

我们正在考虑对一个大型应用程序进行重构,这个应用程序有一个复杂的图形用户界面(GUI),并且它与后端是解耦的。我们想使用新的(Python 2.6)多进程模块。这个GUI和后端之间的接口使用队列(Queues),并且在两个方向上交换消息对象。

我刚刚得出的一个结论(虽然还不太确定,但欢迎大家确认)是,“对象身份”在多进程接口中不会被保留。目前,当我们的GUI向后端发送一条消息时,它希望能收到同样的消息,并且附带一个结果作为属性。它使用对象身份(if received_msg is message_i_sent:)来识别返回的消息,但这在多进程中可能行不通。

这个问题是想请教大家在实际使用中遇到过哪些类似的“坑”,或者你能想象在简单使用多进程模块时会遇到哪些问题,特别是在重构一个已有的单进程应用时。请说明你的回答是否基于实际经验。如果能提供一个可行的解决方法,那就更好了。

编辑:虽然我提这个问题的目的是想收集一些关于问题的描述,但我觉得我犯了两个错误:我一开始就把它设为社区维基(这可能让很多人忽视它,因为他们得不到声望点),而且我提供了一个太具体的例子——虽然我很感激大家的回答,但这可能让很多人错过了我对一般性回应的请求。我可能会重新措辞并在新问题中再次提问。现在我只是接受一个答案作为最佳答案,以便结束这个问题,特别是关于我提供的具体例子。感谢那些回答的人!

3 个回答

1

你可以试试我项目 GarlicSim 里的 persistent 包。它是 LGPL 授权的。

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

里面的主要模块是 persistent.py

我经常这样使用它:

# ...
self.identity = Persistent()

这样我就可以在不同的进程之间保持一个身份。

1

当然,检查非单例对象的身份(比如说“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)

这可能是个不错的主意。

另外,要注意,如果你有很多“工作进程”,那么内存的消耗可能会比基于线程的方法要大得多。

2

我自己没有直接使用过多进程,但遇到的问题和我在其他两个领域的经验很相似:分布式系统和对象数据库。Python中的对象身份既有好处也有坏处!

关于一些常见的问题,如果你正在重构的应用程序能够意识到任务是异步处理的,那就会更好。如果不能,你通常会需要管理锁,这样一来,你本来可以通过使用独立进程获得的性能提升,就会因为等待这些锁而损失掉。我还建议你花时间为跨进程调试建立一些基础设施。真正的异步进程往往会做很多事情,超出我们大脑能理解和验证的范围——至少我自己是这样!

针对具体情况,我会在进程边界管理对象身份,也就是在任务排队和返回时。当发送一个任务去处理时,给这个任务加上一个id(),并把任务实例存放在一个字典里,用id()作为键。当任务更新或完成时,从字典中通过id()取回这个具体的任务,并把新更新的状态应用到它上面。这样,具体的任务及其身份就能得到保持。

撰写回答