【问题标题】:How can I share a class between processes?如何在进程之间共享一个类?
【发布时间】:2015-02-19 17:04:25
【问题描述】:

我想要一个由所有进程共享和更新的全局对象,并且锁定最少。

import multiprocessing

class Counter(object):
  def __init__(self):
    self.value = 0

  def update(self, value):
    self.value += value


def update(counter_proxy, thread_id):
  counter_proxy.value.update(1)
  print counter_proxy.value.value, 't%s' % thread_id, \
    multiprocessing.current_process().name
  return counter_proxy.value.value

def main():
  manager = multiprocessing.Manager()
  counter = manager.Value(Counter, Counter())
  pool = multiprocessing.Pool(multiprocessing.cpu_count())
  for i in range(10):
    pool.apply(func = update, args = (counter, i))
  pool.close()
  pool.join()

  print 'Should be 10 but is %s.' % counter.value.value

if __name__ == '__main__':
  main()

结果是这个 - 不是 10 而是零。看起来对象的共享值没有更新。如何锁定和更新这样的值?

0 t0 PoolWorker-2
0 t1 PoolWorker-3
0 t2 PoolWorker-5
0 t3 PoolWorker-8
0 t4 PoolWorker-9
0 t5 PoolWorker-2
0 t6 PoolWorker-7
0 t7 PoolWorker-4
0 t8 PoolWorker-6
0 t9 PoolWorker-3
Should be 10 but is 0.

@dano 提供的当前最佳解决方案 - 我将自定义管理器与类代理混合在一起。

import multiprocessing
from multiprocessing.managers import BaseManager, NamespaceProxy


class Counter(object):
  def __init__(self):
    self.value = 0

  def update(self, value):
    self.value += value


def update(counter_proxy, thread_id):
  counter_proxy.update(1)

class CounterManager(BaseManager):
  pass

class CounterProxy(NamespaceProxy):
  _exposed_ = ('__getattribute__', '__setattr__', '__delattr__', 'update')

  def update(self, value):
    callmethod = object.__getattribute__(self, '_callmethod')
    return callmethod(self.update.__name__, (value,))

CounterManager.register('Counter', Counter, CounterProxy)

def main():
  manager = CounterManager()
  manager.start()

  counter = manager.Counter()
  pool = multiprocessing.Pool(multiprocessing.cpu_count())
  for i in range(10):
    pool.apply(func = update, args = (counter, i))
  pool.close()
  pool.join()

  print 'Should be 10 but is %s.' % counter.value

if __name__ == '__main__':
  main()

【问题讨论】:

  • 您是如何得知NamespaceProxy 的?我在任何地方都找不到它的文档。

标签: python multiprocessing python-multiprocessing


【解决方案1】:

multiprocessing.Value 并非设计用于自定义类,它应该类似于multiprocessing.sharedctypes.Value。相反,您需要创建一个custom manager 并使用它注册您的课程。如果您不直接访问value,而是通过方法修改/访问它,您的生活也会更轻松,默认情况下会导出为您的班级创建的默认Proxy。常规属性(如Counter.value)不是,因此如果没有额外的自定义,它们就无法访问。这是一个工作示例:

import multiprocessing
from multiprocessing.managers import BaseManager

class MyManager(BaseManager): pass

def Manager():
    m = MyManager()
    m.start()
    return m 

class Counter(object):
  def __init__(self):
    self._value = 0

  def update(self, value):
    self._value += value

  def get_value(self):
      return self._value

MyManager.register('Counter', Counter)

def update(counter_proxy, thread_id):
  counter_proxy.update(1)
  print counter_proxy.get_value(), 't%s' % thread_id, \
    multiprocessing.current_process().name
  return counter_proxy

def main():
  manager = Manager()
  counter = manager.Counter()
  pool = multiprocessing.Pool(multiprocessing.cpu_count())
  for i in range(10):
    pool.apply(func=update, args=(counter, i))
  pool.close()
  pool.join()

  print 'Should be 10 but is %s.' % counter.get_value()

if __name__ == '__main__':
  main()

输出:

1 t0 PoolWorker-2
2 t1 PoolWorker-8
3 t2 PoolWorker-4
4 t3 PoolWorker-5
5 t4 PoolWorker-6
6 t5 PoolWorker-7
7 t6 PoolWorker-3
8 t7 PoolWorker-9
9 t8 PoolWorker-2
10 t9 PoolWorker-8
Should be 10 but is 10.

【讨论】:

  • 感谢您的建议我不知道我可以注册课程 - 看起来不错。我创建了一些这样的替代代码,但不知道为什么它可以工作counter = counter_proxy.get(); counter.update(1); counter_proxy.set(counter) - 只需键入即可。如果您阅读类属性,那不是问题,它可以演变成属性,它很简单,应该使用 - 我没有遵循所有必须在开始时就装备的 java 模式 - 一开始更喜欢简单 - 它允许大型代码而不是 Java 样式.
  • @Chameleon @property 装饰器不适用于默认的 Proxy 类型。如果您真的想直接访问value 属性,请参阅this question 以了解这样做的方法。
  • 看起来很复杂。我需要学习例子。看起来真正的线程是 Python 的弱点 - 没有很好的文档,很少有例子也是问题。好点,我应该使用方法。
  • 不确定,但我认为Manager 应该派生自SyncManager,无论此代码如何工作,我不是该领域的专家,需要学习它。
  • @Chameleon 你只需要让MyManager派生自SyncManager,如果你想使用在SyncManager预注册的类型(dictlistQueue 等)。否则,BaseManager 工作正常。
猜你喜欢
  • 1970-01-01
  • 2012-07-22
  • 2012-12-15
  • 2013-05-07
  • 2020-10-23
  • 2022-01-03
  • 2011-01-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多