2015-02-19 2 views
9

Я хочу иметь глобальный объект, который совместно используется и обновляется всеми процессами с минимальной блокировкой.Как я могу поделиться классом между процессами?

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() 
+0

Как вы узнали о 'NamespaceProxy'? Я не могу найти документацию нигде. – Seanny123

ответ

10

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. 
+0

Спасибо за предложение Я не знаю, что я могу зарегистрировать классы - выглядит красиво. Я создаю альтернативный код вроде этого, но не знаю, почему он работает 'counter = counter_proxy.get(); counter.update (1); counter_proxy.set (counter) '- просто набрав. Это не проблема, если вы читаете атрибуты класса, которые могут быть преобразованы в свойства, это просто ans следует использовать - я не следую шаблонам java, которые все должны быть бронированы при запуске, - сначала попробуйте просто - это позволяет использовать большой код, чем стили Java , – Chameleon

+1

@Chameleon Декодер '@ property' не будет работать с типом' Proxy' по умолчанию. Если вы действительно хотите получить доступ к атрибуту 'value' напрямую, см. [Этот вопрос] (http://stackoverflow.com/questions/26499548/accessing-an-attribute-of-a-multiprocessing-proxy-of-a -class), чтобы сделать это. – dano

+0

Это выглядит сложным. Мне нужны примеры обучения. Похоже, что истинная нить - это слабость Python - не очень хорошая документация с несколькими примерами также является проблемой. Хорошо, что я должен использовать методы. – Chameleon

Смежные вопросы