Pool (). Map: изменить общий объект - PullRequest
0 голосов
/ 01 июня 2018

Рассмотрим следующий игрушечный пример.Я делаю распараллеливание для вычисления значения функции квадрата при внесении некоторых модификаций общего объекта A.

import multiprocessing

A = [1, 2]

def square(i):

    A[i] = 2 + A[i]

    return i * i

square(0)
square(1)

print(A)

A = [1, 2]

multiprocessing.Pool().map(square, [0, 1])

print(A)

Вывод следующий:

[3, 4]
[1, 2]

Но я ожидаю, что этоbe

[3, 4]
[3, 4]

Как указано выше, в последовательной версии функции квадрата удалось изменить A с [1, 2] на [3, 4].Но pool.map не удалось изменить A. Поэтому я спрашиваю, как изменить общий объект с помощью pool (). Map.Заранее спасибо!

1 Ответ

0 голосов
/ 01 июня 2018

Если ваш startmethod равен spawn или forkserver, то A не является общим объектом в первую очередь.И если вы в Windows, spawn является значением по умолчанию и единственным выбором.

Если ваш startmethod равен fork, тогда A может быть общим объектом, но если это так,на самом деле не безопасно изменять его без каких-либо блокировок.

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

Первый вариант - использование общей памяти.В этом случае вы используете свой список в виде массива небольших целых с фиксированным размером, который вы можете смоделировать с помощью Array('i', [1, 2]), который вы можете использовать точно так же, как в примере в документации.В более сложных случаях часто требуется добавить Lock или другой механизм синхронизации для защиты общей памяти.Это довольно эффективно и просто, но работает только тогда, когда ваши общие данные могут быть сопоставлены с низкоуровневыми типами, подобными этому.

Второй вариант - использование Manager.list([1, 2]), которое вы можете использовать точнокак в следующем примере в документации.Это гораздо менее эффективно - оно работает, создавая очередь и передавая сообщения туда и обратно, которые сообщают основному процессу, что нужно выполнять работу всякий раз, когда вы хотите получить доступ к списку или изменить его, - но его преимущество в том, что он прост в использовании.


Но, опять же, обычно лучше не делать ни одну из этих вещей, а вместо этого переписать свой код, чтобы не нуждаться в общих данных в первую очередь.Обычно это означает, что нужно возвращать больше данных из задач пула, а затем заставить основной процесс каким-либо образом собрать возвращаемые значения.Конечно, это сложно, если, например, другие задачи по своей сути должны видеть измененные значения.(В таких случаях вам часто приходится собирать 80% того, что делает Manager, и в этот момент вы можете просто использовать Manager…).Но в вашем игрушечном примере это не так.(И, на самом деле, когда вы думаете , что неизбежно необходимо, это часто означает, что вы не продумали, как недетерминизм повлияет на ваш алгоритм, и все равно не сработало бы ...)

Вот пример того, как вы можете сделать это с вашей игрушкой:

import multiprocessing

def square(i, aval):
    # actual return value, i, and value to set A[i] to
    return i*i, i, 2+aval

A = [1, 2]
# pass each A[i] into the function
for result, i, aval in multiprocessing.Pool().starmap(square, zip([0, 1], A)):
    # get the new A[i] out of the function and store it
    A[i] = aval    
print(A)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...