如何将python dict与多处理同步

时间:2010-03-30 14:26:33

标签: python multiprocessing dictionary

我使用Python 2.6和多处理模块进行多线程处理。现在我想要一个同步的dict(我真正需要的唯一原子操作是值上的+ =运算符)。

我应该使用multiprocessing.sharedctypes.synchronized()调用来包装dict吗?或者是另一种方式?

4 个答案:

答案 0 :(得分:55)

简介

似乎有很多扶手椅建议,没有工作实例。这里列出的答案都没有建议使用多处理,这有点令人失望和令人不安。作为python爱好者,我们应该支持我们的内置库,虽然并行处理和同步从来都不是一件小事,但我相信它可以通过适当的设计变得微不足道。这在现代多核架构中变得非常重要,并且不能过分强调!也就是说,我对多处理库很不满意,因为它仍然处于初期阶段,存在很多陷阱,错误,并且面向功能编程(我讨厌)。目前,由于多处理在服务器运行时无法共享新创建的对象的严重限制,我仍然更喜欢Pyro模块(远远超过它的时间)。管理器对象的“register”类方法只会在管理器(或其服务器)启动之前实际注册一个对象。足够的喋喋不休,更多代码:

Server.py

from multiprocessing.managers import SyncManager


class MyManager(SyncManager):
    pass


syncdict = {}
def get_dict():
    return syncdict

if __name__ == "__main__":
    MyManager.register("syncdict", get_dict)
    manager = MyManager(("127.0.0.1", 5000), authkey="password")
    manager.start()
    raw_input("Press any key to kill server".center(50, "-"))
    manager.shutdown()

在上面的代码示例中,Server.py使用了多处理的SyncManager,它可以提供同步的共享对象。此代码无法在解释器中运行,因为多处理库对于如何为每个注册对象查找“可调用”非常敏感。运行Server.py将启动一个自定义SyncManager,该SyncManager共享syncdict字典以使用多个进程,并且可以在同一台计算机上连接到客户端,或者如果在环回以外的IP地址上运行,则运行其他计算机。在这种情况下,服务器在端口5000上的环回(127.0.0.1)上运行。使用authkey参数在操作syncdict时使用安全连接。当按下任何键时,管理器将关闭。

Client.py

from multiprocessing.managers import SyncManager
import sys, time

class MyManager(SyncManager):
    pass

MyManager.register("syncdict")

if __name__ == "__main__":
    manager = MyManager(("127.0.0.1", 5000), authkey="password")
    manager.connect()
    syncdict = manager.syncdict()

    print "dict = %s" % (dir(syncdict))
    key = raw_input("Enter key to update: ")
    inc = float(raw_input("Enter increment: "))
    sleep = float(raw_input("Enter sleep time (sec): "))

    try:
         #if the key doesn't exist create it
         if not syncdict.has_key(key):
             syncdict.update([(key, 0)])
         #increment key value every sleep seconds
         #then print syncdict
         while True:
              syncdict.update([(key, syncdict.get(key) + inc)])
              time.sleep(sleep)
              print "%s" % (syncdict)
    except KeyboardInterrupt:
         print "Killed client"

客户端还必须创建一个自定义的SyncManager,注册“syncdict”,这次没有传入一个callable来检索共享的dict。然后,它使用定制的SycnManager使用端口5000上的环回IP地址(127.0.0.1)进行连接,并使用authkey建立与Server.py中启动的管理器的安全连接。它通过调用管理器上注册的callable来检索共享的dict syncdict。它会提示用户输入以下内容:

  1. 用于操作的同步键
  2. 每个周期增加密钥访问值的数量
  3. 每个周期的睡眠时间(以秒为单位)
  4. 然后客户端检查密钥是否存在。如果不是,它会在syncdict上创建密钥。然后客户端进入一个“无限”循环,它通过增量更新键的值,睡眠指定的数量,并打印syncdict只重复此过程,直到发生KeyboardInterrupt(Ctrl + C)。

    恼人的问题

    1. 必须在管理器启动之前调用管理器的注册方法,否则即使管理器上的dir调用显示它确实具有已注册的方法,您也会获得异常。
    2. dict的所有操作都必须使用方法而不是dict赋值(syncdict [“blast”] = 2会因为多处理共享自定义对象的方式而失败)
    3. 使用SyncManager的dict方法可以缓解恼人的问题#2,除了恼人的问题#1阻止SyncManager.dict()返回的代理被注册和共享。 (SyncManager.dict()只能在管理器启动后调用,并且注册只能在管理器启动之前工作,因此SyncManager.dict()仅在进行函数式编程并将代理作为参数传递给Processes时才有用。 doc examples do)
    4. 服务器和客户端都必须注册,即使直观地看起来客户端在连接到管理器之后就能解决这个问题(请将此添加到您的愿望清单多处理开发人员)
    5. 我希望你能像我一样享受这个相当彻底且稍微耗时的答案。我很难直接理解为什么我在多处理模块中苦苦挣扎,Pyro让它变得轻而易举,而现在由于这个答案,我已经敲了敲头。我希望这对于如何改进多处理模块的python社区是有用的,因为我相信它有很大的希望,但在它的初期缺乏可能性。尽管描述了令人烦恼的问题,但我认为这仍然是一个非常可行的替代方案并且非常简单。您也可以使用SyncManager.dict()并将其作为参数传递给Processes,就像文档显示的方式一样,它可能是一个更简单的解决方案,这取决于您的要求,这对我来说感觉不自然。

答案 1 :(得分:4)

我会专门用一个单独的程序来维护“共享字典”:只需使用例如xmlrpclib使其他进程可以使用少量代码,通过xmlrpclib公开,例如一个函数,key, increment执行增量,一个只使用key并返回值,具有语义详细信息(缺少键的默认值等等),具体取决于您的应用程序的需求。

然后你可以使用你喜欢的任何方法来实现shared-dict专用进程:从内存中有简单dict的单线程服务器到简单的sqlite DB等等,我建议你开始使用代码“尽可能简单”(取决于您是否需要持久性共享字典,或者持久性对您来说不是必需的),然后根据需要进行测量和优化。

答案 2 :(得分:4)

响应并发写入问题的适当解决方案。我做了很快的研究,发现this article建议使用锁定/信号量解决方案。 (http://effbot.org/zone/thread-synchronization.htm

虽然示例不是字典上的特异性,但我很确定您可以编写基于类的包装器对象来帮助您根据这个想法使用字典。

如果我需要以线程安全的方式实现这样的东西,我可能会使用Python Semaphore解决方案。 (假设我之前的合并技术不起作用。)我认为信号量通常会因为它们的阻塞性而降低线程效率。

来自网站:

  

信号量是一种更先进的锁机制。信号量具有内部计数器而不是锁定标志,并且只有在超过给定数量的线程试图保持信号量时它才会阻塞。根据信号量的初始化方式,这允许多个线程同时访问相同的代码段。

semaphore = threading.BoundedSemaphore()
semaphore.acquire() # decrements the counter
... access the shared resource; work with dictionary, add item or whatever.
semaphore.release() # increments the counter

答案 3 :(得分:3)

是否有理由首先需要共享字典?您是否可以让每个线程都维护自己的字典实例,并在线程处理结束时进行合并,或者定期使用回调将各个线程字典的副本合并在一起?

我不确切知道你在做什么,所以请保持我的书面计划可能不会逐字逐句。我的建议更多的是高级设计理念。