Как использовать multiprocessing.Manager (). Value для хранения суммы? - PullRequest
2 голосов
/ 12 марта 2020

Я хочу накапливать сумму, используя мультипроцессинг. Вот как я пытался:

import multiprocessing

def add_to_value(addend, value):
    value.value += addend

with multiprocessing.Manager() as manager:
    value = manager.Value(float, 0.0)
    with multiprocessing.Pool(2) as pool:
        pool.starmap(add_to_value,
                     [(float(i), value) for i in range(100)])
    print(value.value)

Это дает неверные и даже противоречивые результаты. Например, один раз он дает 2982,0, а другой - 2927,0. Правильный вывод 4950.0, и я получаю это, когда использую только один процесс при вызове пула, а не 2. Я использую Python 3.7.5.

1 Ответ

2 голосов
/ 12 марта 2020

В многопроцессорной документации (под multiprocessing.Value) об этом совершенно ясно сказано:

Операции типа +=, которые включают чтение и запись, не являются атомами c , Так, например, если вы хотите атомарно увеличить общее значение, недостаточно просто сделать counter.value += 1.

Короче говоря, вам нужно захватить блокировку, чтобы иметь возможность сделать это.

Вы можете сделать это с помощью:

def add_to_value(addend, value, lock):
    with lock:
        value.value += addend

if __name__ == '__main__':
    with multiprocessing.Manager() as manager:
        lock = manager.Lock()
        value = manager.Value(float, 0.0)
        with multiprocessing.Pool(2) as pool:
            pool.starmap(add_to_value,
                         [(float(i), value, lock) for i in range(100)])
        print(value.value)

Это будет правильно выводить 4950.0.

Но учтите, что этот подход будет довольно дорогим из-за необходимости блокировки. Скорее всего, это займет больше времени, чтобы завершить sh, чем если бы у вас был один процесс, выполняющий операцию.

ПРИМЕЧАНИЕ: Я также добавляю if __name__ == '__main__': guard, который на самом деле требуется при использовании метода запуска, отличного от fork . По умолчанию для ОС Windows и Ma c установлено spawn , поэтому действительно необходимо сделать этот код переносимым на любую из этих платформ. Методы запуска spawn и forkserver также доступны в Linux / Unix, поэтому в некоторых ситуациях это также необходимо.

Многопроцессорная обработка будет более эффективной, если вы Вы можете разгрузить работу для работников, которую они могут выполнить самостоятельно, например, рассчитать частичные суммы и затем сложить их вместе в основном процессе. Если возможно, подумайте над тем, чтобы переосмыслить свой подход к этой модели.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...