В чем преимущество использования управления контекстом с многопроцессорной обработкой.Менеджер?

#python #python-3.x #multiprocessing #contextmanager

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

Вопрос:

В документации Manager используется с context manager (т. Е. with ) примерно так:

 from multiprocessing.managers import BaseManager

class MathsClass:
    def add(self, x, y):
        return x   y
    def mul(self, x, y):
        return x * y

class MyManager(BaseManager):
    pass

MyManager.register('Maths', MathsClass)

if __name__ == '__main__':
    with MyManager() as manager:
        maths = manager.Maths()
        print(maths.add(4, 3))         # prints 7
        print(maths.mul(7, 8))         # prints 56
  

Но в чем преимущество этого, за исключением пространства имен? Для открытия файловых потоков преимущество совершенно очевидно в том, что вам не нужно вручную .close() устанавливать соединение, но что это для Manager? Если вы не используете его в контексте, какие шаги вы должны использовать, чтобы убедиться, что все закрыто правильно?

Короче говоря, в чем преимущество использования вышеупомянутого над чем-то вроде:

 manager = MyManager()
maths = manager.Maths()
print(maths.add(4, 3))         # prints 7
print(maths.mul(7, 8))         # prints 56
  

Ответ №1:

Но в чем преимущество этого (…)?

Во-первых, вы получаете основное преимущество практически любых контекстных менеджеров. У вас есть четко определенное время жизни ресурса. Он выделяется и приобретается при открытии with ...: блока. Он освобождается, когда блоки заканчиваются (либо по достижении конца, либо из-за возникновения исключения). Он по-прежнему освобождается всякий раз, когда сборщик мусора обращается к нему, но это вызывает меньшее беспокойство, поскольку внешний ресурс уже выпущен.

В случае multiprocessing.Manager (который представляет собой функцию, которая возвращает a SyncManager , хотя Manager и выглядит как класс), ресурс представляет собой «серверный» процесс, который содержит состояние и ряд рабочих процессов, которые разделяют это состояние.

в чем [преимущество использования context manager] для Manager?

Если вы не используете context manager и не вызываете shutdown для менеджера, то процесс «server» будет продолжаться до тех пор, пока SyncManager __del__ не будет запущен. В некоторых случаях это может произойти вскоре после завершения кода, который создал SyncManager (например, если он создается внутри короткой функции, и функция возвращается нормально, и вы используете CPython, тогда система подсчета ссылок, вероятно, быстро заметит, что объект мертв, и вызовет его __del__ ). В других случаях это может занять больше времени (если возникает исключение и сохраняется ссылка на manager, оно будет сохраняться до тех пор, пока это исключение не будет обработано). В некоторых плохих случаях это может вообще никогда не произойти (если SyncManager заканчивается ссылочным циклом, то __del__ это вообще помешает сборщику циклов собирать его; или ваш процесс может завершиться сбоем до __del__ вызова). Во всех этих случаях вы отказываетесь от контроля над тем, когда очищаются дополнительные процессы Python, созданные с помощью SyncManager . Эти процессы могут представлять нетривиальное использование ресурсов в вашей системе. В действительно плохих случаях, если вы создаете SyncManager в цикле, вы можете в конечном итоге создать многие из них, которые работают одновременно и могут легко потреблять огромное количество ресурсов.

Если вы не используете его в контексте, какие шаги вы должны использовать, чтобы убедиться, что все закрыто правильно?

Вы должны самостоятельно реализовать протокол context manager, как и для любого context manager, который вы использовали без with него. Это сложно сделать на чистом Python, оставаясь при этом корректным. Что-то вроде:

 manager = None
try:
    manager = MyManager()
    manager.__enter__()
    # use it ...
except:
    if manager is not None:
        manager.__exit__(*exc_info())
else:
    if manager is not None:
        manager.__exit__(None, None, None)
  

start и shutdown также являются псевдонимами __enter__ и __exit__ , соответственно.

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

1. Вопрос возник потому, что я видел много фрагментов кода, которые просто инициализировали manager ( manager = Manager() ), но никогда ничего не делали для выхода из управления (как в моем последнем примере). Я понимаю, что с помощью with-block вы автоматически очищаете мусор, но является ли закрытие / выход из менеджера явно необходимым, если вы не используете context manager? Каковы последствия бездействия? manager.__exit__

2. добавлен еще один абзац, в котором это обсуждается более непосредственно

3. Спасибо. Последний вопрос (может быть, лучше подходит для отдельного вопроса?): могут ли Manager и Pool использоваться одним и тем же context manager? Есть ли приоритет, который необходимо учитывать? with Pool(12) as pool, Manager() as manager или наоборот? Или это не имеет значения?

4. Да, мне кажется, это другой вопрос. Я не уверен, как именно взаимодействуют Manager и Pool, и я не нахожу документы настолько понятными. Я предполагаю , что вы можете сделать что-то подобное этому with утверждению, но что именно оно делает , я не знаю. 😉

5. Отличный ответ! Хотя я думаю, что вы не стали бы использовать __enter__() and __exit__() вручную, если не используете context manager. Вы, вероятно, просто использовали start() бы then и вызывали shutdown() finally предложение. Однако, конечно, это зависит от того, что делает context manager (для файлов это отличается от этого случая). Но ваш пример может наглядно продемонстрировать общую работу context manager 🙂