multiprocessing模块与pyro的比较?

8 投票
1 回答
4302 浏览
提问于 2025-04-15 13:06

我在一个计算集群上使用 pyro 来管理并行任务。最近我换到了一个新的集群,在这里我需要负责使用每个计算节点上的所有核心。(在之前的集群中,每个核心都是一个独立的节点。)我觉得 Python 的 multiprocessing 模块很适合这个需求。我注意到它也可以用于 远程进程通信。如果有人同时使用过这两个框架进行远程进程通信,我很想知道它们之间的比较。multiprocessing 模块的一个明显优点是从 2.6 版本开始就已经内置了。除此之外,我很难判断哪个更好。

1 个回答

15

编辑:我在这里修改我的回答,希望能让你们少受点苦。多进程(multiprocessing)还不够成熟,BaseManager的文档是错误的,如果你是一个喜欢面向对象编程的人,想要在运行时动态创建共享对象,请使用PYRO,否则你会非常后悔! 如果你只是想用共享队列做一些功能性编程,像那些愚蠢的例子那样提前注册,那就祝你好运。

简短回答

多进程:

  • 做面向对象的远程对象感觉很别扭
  • 加密(authkey)很简单
  • 可以在网络上或进程间通信
  • 没有像Pyro那样的额外麻烦的名字服务器(不过有方法可以绕过这个)
  • 编辑:一旦管理器实例化后,不能“注册”对象!!?
  • 编辑:如果服务器没有启动,客户端会抛出“无效参数”的异常,而不是简单地说“连接失败”,这是什么鬼!?
  • 编辑:BaseManager的文档是错误的!根本没有“start”方法!?!
  • 编辑:几乎没有使用示例。

Pyro:

  • 简单的远程对象
  • 仅限网络通信(如果只是本地则是回环)
  • 编辑:这个东西就是好用,而且它喜欢面向对象的共享,这让我很喜欢它
  • 编辑:为什么这个不是标准库的一部分,而是那个试图模仿却失败的多进程垃圾?

编辑:我第一次回答这个问题时刚接触2.6版本的多进程。在下面的代码中,Texture类被注册并作为代理共享,但里面的“data”属性却没有。所以你猜会发生什么,每个进程都有一个单独的“data”属性副本,尽管你可能会期待不一样。我花了无数小时试图找出在运行时创建共享对象的好方法,但总是碰壁。这真让人困惑和沮丧。也许只是我,但看看别人尝试的稀少例子,似乎并不是这样。

我不得不痛苦地决定放弃多进程库,选择Pyro,直到多进程更成熟。虽然最开始我很兴奋Python内置了多进程,但现在我对它彻底失望,宁愿多次愉快地安装Pyro库,因为它是Python中如此美丽的库。

详细回答

我在过去的项目中使用过Pyro,感觉非常好。我也开始使用2.6新出的多进程。

在多进程中,我发现动态创建共享对象有点别扭。看起来在它的早期阶段,多进程模块更倾向于功能性编程,而不是面向对象。不过这并不完全正确,因为其实是可以做到的,只是我觉得“注册”调用让我感到受限。

例如:

manager.py:

from multiprocessing import Process
from multiprocessing.managers import BaseManager

class Texture(object):
   def __init__(self, data):
        self.data = data

   def setData(self, data):
      print "Calling set data %s" % (data)
      self.data = data

   def getData(self):
      return self.data

class TextureManager(BaseManager):
   def __init__(self, address=None, authkey=''):
      BaseManager.__init__(self, address, authkey)
      self.textures = {}

   def addTexture(self, name, texture):
      self.textures[name] = texture

   def hasTexture(self, name):
      return name in self.textures

server.py:

from multiprocessing import Process
from multiprocessing.managers import BaseManager
from manager import Texture, TextureManager

manager = TextureManager(address=('', 50000), authkey='hello')

def getTexture(name):
   if manager.hasTexture(name):
      return manager.textures[name]
   else:
      texture = Texture([0]*100)
      manager.addTexture(name, texture)
      manager.register(name, lambda: texture)

TextureManager.register("getTexture", getTexture)


if __name__ == "__main__":
   server = manager.get_server()
   server.serve_forever()

client.py:

from multiprocessing import Process
from multiprocessing.managers import BaseManager
from manager import Texture, TextureManager

if __name__ == "__main__":
   manager = TextureManager(address=('127.0.0.1', 50000), authkey='hello')
   manager.connect()
   TextureManager.register("getTexture")
   texture = manager.getTexture("texture2")
   data = [2] * 100
   texture.setData(data)
   print "data = %s" % (texture.getData())

我所说的别扭感来自server.py,在这里我注册了一个getTexture函数,用于从TextureManager中获取某个名称的函数。回想起来,如果我把TextureManager做成一个可共享的对象,用于创建/获取可共享的纹理,可能就能消除这种别扭感。嗯,我还在尝试,但你明白我的意思。我不记得在使用Pyro时遇到过这种别扭感,但可能有比上面的例子更简洁的解决方案。

撰写回答