Слишком долго; не читал
warnings.catch_warnings()
контекстный менеджер не потокобезопасен. Как использовать его в среде параллельной обработки?
Фон
В приведенном ниже коде решается проблема максимизации с использованием параллельной обработки с помощью модуля Python multiprocessing
. Он принимает список (неизменных) виджетов, разбивает их на разделы (см. Эффективная многопроцессорность массивной максимизации грубой силы в Python 3), находит максимумы ( "финалисты" ) из всех разделов, а затем находит максимум ( "чемпион" ) этих "финалистов". Если я правильно понимаю свой собственный код (и меня бы здесь не было, если бы я это сделал), я делюсь памятью со всеми дочерними процессами, чтобы предоставить им входные виджеты, а multiprocessing
использует трубку уровня операционной системы и маринование, чтобы отправить финалистские виджеты обратно в основной процесс, когда рабочие сделаны.
Источник проблемы
Я хочу уловить избыточные предупреждения виджетов, вызванные повторной инстанцией виджетов после рассыпания, которое происходит, когда виджеты выходят из межпроцессного канала. Когда объекты виджета создают экземпляр, они проверяют свои собственные данные, испуская предупреждения из стандартного модуля warnings
Python, чтобы сообщить пользователю приложения, что виджет подозревает, что есть проблема с входными данными пользователя. Поскольку unpickling заставляет объекты создавать экземпляр, мое понимание кода подразумевает, что каждый объект виджета реинсталлируется ровно один раз, если и только если он является финалистом после того, как он выходит из канала - см. Следующий раздел, чтобы понять, почему это неверно.
Виджеты уже были созданы до того, как они были frobnicated, поэтому пользователь уже мучительно осознает, какой вклад он получил неправильно, и не хочет снова слышать об этом. Это предупреждения, которые я хотел бы уловить с помощью менеджера контекста warnings
module catch_warnings()
(т.е. Инструкции with
).
Неудавшиеся решения
В моих тестах я сузился, когда лишние предупреждения издаются в любом месте между тем, что я обозначил ниже как линия А и строка В. Меня удивляет то, что предупреждения высылаются в местах, отличных от ближайших output_queue.get()
. Это означает, что multiprocessing
отправляет виджеты рабочим, использующим травление.
Результатом является то, что создание менеджера контекста, созданного warnings.catch_warnings()
даже вокруг всего, от линии А до строки В и установки правильных предупреждений фильтр внутри этого контекста не улавливает предупреждения. Это означает, что предупреждения производятся в рабочих процессах. Помещение этого менеджера контекста вокруг рабочего кода также не вызывает предупреждений.
Код
В этом примере отсутствует код для определения того, является ли размер проблемы слишком малым, чтобы беспокоиться о процессах forking, импортировать многопроцессорность и определять my_frobnal_counter
и my_load_balancer
.
"Call `frobnicate(list_of_widgets)` to get the widget with the most frobnals"
def frobnicate_parallel_worker(widgets, output_queue):
resultant_widget = max(widgets, key=my_frobnal_counter)
output_queue.put(resultant_widget)
def frobnicate_parallel(widgets):
output_queue = multiprocessing.Queue()
# partitions: Generator yielding tuples of sets
partitions = my_load_balancer(widgets)
processes = []
# Line A: Possible start of where the warnings are coming from.
for partition in partitions:
p = multiprocessing.Process(
target=frobnicate_parallel_worker,
args=(partition, output_queue))
processes.append(p)
p.start()
finalists = []
for p in processes:
finalists.append(output_queue.get())
# Avoid deadlocks in Unix by draining queue before joining processes
for p in processes:
p.join()
# Line B: Warnings no longer possible after here.
return max(finalists, key=my_frobnal_counter)