#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 🙂