Создание переменной, которую можно сравнивать между процессами

#python #process #python-3.x #multiprocessing

#python #процесс #python-3.x #многопроцессорная обработка

Вопрос:

У меня есть код, подобный следующему,

 class _Process(multiprocessing.Process):

    STOP = multiprocessing.Manager().Event()

    def __init__(self, queue, process_fn):

        self._q = queue
        self._p = process_fn
        super().__init__()

    def run(self):

        while True:
            dat = self._q.get()
            if not dat is _Process.STOP:
                self._p(dat, self._q)
                self._q.task_done()
            else:
                self._q.task_done()
                break
  

но я не могу STOP успешно сравнить. Это неудивительно, когда я использую is , поскольку, я полагаю, is сравнивает идентификаторы объектов и из документов «… Это адрес объекта в памяти «.Итак, поскольку я использую несколько процессов, адрес памяти будет другим. (Я не могу сравнить это ни с == тем, ни с другим, и я не уверен, почему это так).

Это происходит с любым объектом, с которым я создаю Manager() , но если я использую «истинный» синглтон ( True или False или None ), он работает. Хотя это не подходящее решение, поскольку любое из этих значений может быть допустимым в очереди.

Итак, как я могу создать переменную, подобную синглетонам, которую можно сравнивать между процессами?

(Примечание: Я тоже пытался использовать выделенный класс, но получаю ошибки о том, что его невозможно обработать.)

Обновление: ответ, похоже, заключается в использовании класса, но я получал проблемы с маринованием, поскольку я пытался только с внутренним классом. Перемещение ее в область модуля исправило ошибку, и она работает нормально. — Спасибо @Schnouki!


Вот пример (и бессмысленный) использования кода, который показывает ошибку …

 def f(data, queue):
    print(data)

q = multiprocessing.JoinableQueue()

for i in range(4):
    p = _Process(q, f)
    p.daemon = True
    p.start()
    q.put(i)

q.join()

for i in range(4):
    q.put(_Process.STOP)

q.join()
  

Ответ №1:

Это странный способ использования Event объекта… Если вы не можете использовать None или логическое значение, я предлагаю вам использовать выделенный класс и протестировать тип того, что вы получаете из очереди:

 class StopProcessing(object):
    pass

#...

q.put(StopProcessing())

#...

while True:
    dat = self._q.get()
    if type(dat) is StopProcessing:
        # ...
  

Или, конечно, вы могли бы просто продолжать использовать multiprocessing.Event и проверять его тип. Однако это, вероятно, будет вводить в заблуждение кого-то другого, читающего ваш код; использование выделенного типа кажется мне намного более чистым и питоническим.

РЕДАКТИРОВАТЬ: Хорошо, так что, по-видимому, это не работает, потому что новый класс не может быть выбран. Итак, вот еще одна идея: что, если вы напрямую поместите тип в свою очередь, вот так:

 class StopProcessing(object):
    pass
#...
q.put(StopProcessing)
#...
while True:
    dat = self._q.get()
    if dat is StopProcessing:
        #...
  

Согласно документу pickle, «классы, определенные на верхнем уровне модуля», могут быть изменены.

Комментарии:

1. Спасибо, я просто использовал Event в качестве примера. Как я уже сказал, это происходит с любым из объектов, возвращаемых Manager , я бы, вероятно, просто использовал Value в конце дня, если бы это сработало (не совсем уверен, почему я этого не сделал в примере!). Спасибо за предложение об использовании класса, я должен был упомянуть, я уже пробовал это и получаю сообщение об ошибке о том, что класс не может быть маринован. Я обновил вопрос, чтобы упомянуть об этом.

2. Что, если вы напрямую поместите тип в свою очередь, например q.put(StopProcessing) , и затем протестируете его с if dat is StopProcessing помощью? Согласно документу pickle , «классы, определенные на верхнем уровне модуля», могут быть изменены.

3. Ах! Я пробовал это, добавив только тип, да. На самом деле это было первое, что я попробовал, прежде чем возиться с Manager объектами, но у меня всегда был класс как внутренний класс. Включение ее в область действия модуля позволяет ей работать идеально. Большое вам спасибо. Интересно, не могли бы вы добавить этот совет к своему ответу, чтобы он соответствовал моему обновлению в вопросе.