Python: Почему многопроцессорная блокировка здесь используется совместно?
Я пытаюсь разделить блокировку между процессами. Я понимаю, что способ поделиться блокировкой - это передать ее в качестве аргумента целевой функции. Однако я обнаружил, что даже подход ниже работает. Я не мог понять, как процессы разделяют эту блокировку. Может ли кто-нибудь объяснить, пожалуйста?
import multiprocessing as mp
import time
class SampleClass:
def __init__(self):
self.lock = mp.Lock()
self.jobs = []
self.total_jobs = 10
def test_run(self):
for i in range(self.total_jobs):
p = mp.Process(target=self.run_job, args=(i,))
p.start()
self.jobs.append(p)
for p in self.jobs:
p.join()
def run_job(self, i):
with self.lock:
print('Sleeping in process {}'.format(i))
time.sleep(5)
if __name__ == '__main__':
t = SampleClass()
t.test_run()
2 ответа
В Windows (которую вы сказали, что используете), такие вещи всегда сводятся к деталям о том, как multiprocessing
играет с pickle
потому что все границы процесса пересечения данных Python в Windows реализуются путем травления на передающей стороне (и отмены травления на принимающей стороне).
Мой лучший совет - избегать действий, которые вызывают такие вопросы, для начала;-) Например, код, который вы показали, взрывается в Windows под Python 2, а также взрывается под Python 3, если вы используете multiprocessing.Pool
метод вместо multiprocessing.Process
,
Это не просто блокировка, просто попытка привязать связанный метод (например, self.run_job
) взрывается в Python 2. Подумайте об этом. Вы пересекаете границу процесса, и нет объекта, соответствующего self
на приемном конце. К какому объекту относится self.run_job
должен быть связан на приемном конце?
В Python 3 травление self.run_job
также мариновать копию self
объект. Вот и ответ: SampleClass
объект, соответствующий self
создается магией на приемном конце. Ясно, как грязь. t
маринованное, в том числе t.lock
, Вот почему это "работает".
Смотрите это для более подробной информации о реализации:
Почему я могу передать метод экземпляра multiprocessing.Process, но не multiprocessing.Pool?
В конечном счете, вы будете страдать от наименьшего количества загадок, если будете придерживаться вещей, которые явно предназначались для работы: передавать вызываемые модулем глобальные объекты (ни, например, методы экземпляра или локальные функции), и явно передавать multiprocessing
объекты данных (будь то экземпляр Lock
, Queue
, manager.list
и тд и тп).
В операционных системах Unix новые процессы создаются через fork
примитивный.
fork
Примитив работает путем клонирования адресного пространства памяти родительского процесса, присваивая его потомку. У потомка будет копия памяти родителя, а также файловых дескрипторов и общих объектов.
Это означает, что когда вы вызываете fork, если у родительского файла открыт файл, у дочернего элемента он тоже будет. То же самое относится к общим объектам, таким как трубы, сокеты и т. Д.
В Unix+CPython,Locks
реализуются черезsem_open
примитив, который предназначен для совместного использования при разветвлении процесса.
Я обычно рекомендую не смешивать параллелизм (в частности, многопроцессорность) и ООП, потому что это часто приводит к недоразумениям такого рода.
РЕДАКТИРОВАТЬ:
Видел только сейчас, что вы используете Windows. Tim Peters дал правильный ответ. Ради абстракции Python пытается обеспечить независимое поведение ОС через свой API. При вызове метода экземпляра он выбирает объект и отправляет его по каналу. Таким образом, обеспечивая поведение, подобное для Unix.
Я бы порекомендовал вам прочитать руководство по программированию для многопроцессорной обработки. Ваша проблема решена, в частности, в первом пункте:
Избегайте общего состояния
Насколько это возможно, следует избегать смещения больших объемов данных между процессами.
Вероятно, лучше всего использовать очереди или каналы для связи между процессами, а не использовать примитивы синхронизации более низкого уровня.