#python #concurrency #multiprocessing #subprocess
Вопрос:
Предположим, я использую a multiprocessing.Manager
для создания двух общих объектов и передачи их подпроцессу, который начинает запись в эти объекты:
manager = multiprocessing.Manager()
a = manager.list()
b = manager.list()
subprocess = MyProcess(a,b)
subprocess.start() # writing to a and b starts here
# inspect a and b - in what order will changes appear?
Есть ли способ убедиться, что порядок, в котором эти записи отражаются в родительском процессе, совпадает с порядком, в котором записи выполняются в подпроцессе? Что делать, если я register()
класс с двумя членами, и подпроцесс вносит изменения в эти два члена?
Есть ли ссылка, которая отвечает на эти вопросы «порядка операций» в более общем плане? (Я не смог найти это в документах). Например: что, если мы создадим второй подпроцесс subprocess_2
, который также будет выполнять некоторую запись и чтение a
, и b
— что мы можем сказать об изменениях порядка, которые будут отражены в родительском и в subprocess_2
?
ПРОСТОЙ, ПОДРОБНЫЙ ПРИМЕР: Следуя примеру Booboo, если myProcess (подкласс multiprocessing.Process
) реализует run()
так:
def run(self) :
a.append(1)
b.append(2)
затем, если мы подождем достаточно долго, мы знаем, что родительский процесс увидит a == [1]
и b == [2]
. Вопрос в том, какие возможные состояния мы можем видеть между ними. Если в менеджере есть какая-то глобальная синхронизация, мы сможем увидеть только следующие пары значений для a,b
: [],[]
, [1],[]
или конечного состояния [1],[2]
. Но без такой синхронизации мы можем мельком [],[2]
увидеть (если, например, сообщение о добавлении к b
достиг родителя быстрее, или порядок опроса очередей не такой, как мы ожидаем (чего мы ожидаем?)). Я надеялся, что мне не придется просматривать исходный код (который также может измениться в будущих версиях), а скорее получить общую гарантию, если она есть. Надеюсь, это прояснит вопрос.
Комментарии:
1. Я понимаю, что, возможно, это не лучший ответ, но многопоточность и особенно синхронизация, обмен данными между потоками не являются самыми сильными сторонами Python. Вы выбираете правильный инструмент для этой цели?
2. Основная вычислительная работа в моем приложении выполняется на Java, и я думаю, что именно там я привык получать точные гарантии порядка операций (предоставляемые «моделью памяти java»). Оболочка python делает что — то довольно простое. В python я использую параллелизм только как способ получить большую гибкость в дизайне.
Ответ №1:
В показанном примере вы имеете дело с управляемым списком. Этот список существует в процессе, который создается при выполнении manager = multiprocessing.Manager()
. Переменные a
и b
на самом деле являются прокси-объектами. Когда методы вызываются на этих прокси-серверах, выполняется вызов удаленного метода (механизм связи представляет собой сокет под Linux и именованный канал под Windows) из адресного пространства одного процесса в адресное пространство SynchManager ( multiprocessing.SyncManager
будучи классом, который создается вызовом multiprocessing.Manager()
), и фактический метод выполняется потоком, запущенным в адресном пространстве SyncManager, прослушивающим соединение сокета, и не будет отвечать на соединение (возвращаться обратно вызывающему), пока вызов метода не будет завершен.
Обновить
Основываясь на обновленном вопросе OP, возможные состояния списков a
и b
расположены в порядке времени:
a
-> [],b
-> []a
-> [1],b
-> []a
-> [1],b
-> [2]
Других возможных состояний нет, потому что вызов a.append(1)
будет заблокирован до тех a
пор, пока список не будет дополнен значением 1
. Представьте себе следующий код:
a.append(1)
# How can the following assertion fail?
# Who would implement a list in such a way where this could fail?
assert(1 in a)
b.append(b)
Комментарии:
1. когда я это сделаю
a.append(1)
,b.append(2)
я ожидаю, что по прошествии достаточно долгого времени найдуa == [1]
иb == [2]
, возможно, вы захотите исправить это в своем ответе (вы написали «…список теперь будет[1,2]
«), Но далеко не очевидно, что произойдет, если пройдет короткое время. Если есть какая-то глобальная блокировка объектов диспетчера, я ожидаю , что единственными возможными видимыми состояниями будут[],[]
[1],[]
или[1],[2]
. Но в целом, я вполне могу мельком[],[2]
увидеть . Надеюсь, это прояснит вопрос.2. Я хотел сказать
a,append(2)
(неb.append(2)
) в первом абзаце-я отредактировал ответ). Вот почему я сказал: «Список теперь будет[1, 2]
«. Поэтому во втором абзаце моего ответа я просто имел дело с тем, как один из списков. например, списокa
, может закончиться предположением, что оба процесса записывают 1 и 2a
в таком порядке. Так что, пожалуйста, забудь обb.append(2)
этом . Я не делал никаких предположений о том, что записывается во второй список. Вы не показали, как процесс вносит свои изменения в два списка. Например, они чередуются или нет?3. Теперь я добавил разъяснение к вопросу, используя ваш пример
4. Я посмотрю.
5. и в качестве прекрасного примера состояния гонки, это ваш пример «предварительного редактирования», в котором мы пишем как a, так и b 🙂