ThreadPoolExecutor и ProcessPoolExecutor: загрузка и обработка данных
Напишите решение на Python и проверьте его на странице.
Другие упражнения по этой теме: Concurrent futures Python: задачи на ThreadPoolExecutor и ProcessPoolExecutor.
Конвейер карты высот
Геоинформационный сервис строит цифровую карту высот, распределяя вычисления между двумя исполнителями. Сначала сырые фрагменты рельефа загружаются из сетевого хранилища через ThreadPoolExecutor. После успешного скачивания каждый фрагмент отправляется на изолированную интерполяцию в ProcessPoolExecutor, чтобы ресурсоёмкие математические расчеты выполнялись в отдельных процессах.
Однако некоторые варианты алгоритмов интерполяции передаются в виде локальных функций или замыканий. При попытке получить результат из их Future в пуле процессов возникает отложенная ошибка сериализации pickle. Сервис строго разделяет причины сбоев: если проверка упавшей задачи подтверждает, что не удалось сериализовать аргументы или саму вызываемую функцию, сервис запускает для этого фрагмента запасную триангуляцию в ThreadPoolExecutor и отмечает использование резерва. Если же внутри алгоритма произошла вычислительная ошибка, она считается неустранимым браком фрагмента и не должна приводить к запуску резервных вычислений.
Реализуйте конвейер взаимодействия двух пулов, который выполняет основную интерполяцию в процессах, при доказанном сбое сериализации переключается на запасной расчет в потоках, а при внутренних ошибках алгоритма фиксирует исключение без запуска резерва.
Что уже дано
В среде выполнения уже определены вспомогательные диагностические функции и фабрики наблюдений:
is_submission_serialization_failure(future)— возвращаетTrue, если задача пула процессов завершилась доказанным системным сбоем сериализации переданной функции или её аргументов при отправке в очередь воркеров;make_fallback_example_case(specifications)— контекстный менеджер для локального тестирования конвейера, принимающий словарь спецификаций фрагментов. Внутри контекста доступны готовые колбэкиfetch,interpolate(вычисляет сумму квадратов значений фрагмента),backup(вычисляет обычную сумму значений фрагмента), а также действующие пулыio_poolиcpu_pool;observe_fallback_probe(specifications)иmake_fallback_example_observation()— инструменты для проверки работы конвейера.
Вам предстоит написать тело функции build_height_map.
Что нужно сделать
Реализуйте функцию build_height_map(fragment_ids, fetch, interpolators, backup, io_pool, cpu_pool).
Функция принимает шесть аргументов:
fragment_ids— список уникальных целочисленных идентификаторов фрагментов (от 0 до 12 идентификаторов);fetch— функциюfetch(fragment_id), возвращающую сырые данные фрагмента (содержит от 0 до 32 целочисленных значений в диапазоне от -1000 до 1000);interpolators— отображение идентификаторов фрагментов на соответствующие им функции основной интерполяции от одного аргументаinterpolator(fragment);backup— функциюbackup(fragment)для резервного расчета в пуле потоков из того же скачанного объекта фрагмента;io_pool— экземплярconcurrent.futures.ThreadPoolExecutorдля скачивания и резервных расчетов;cpu_pool— экземплярconcurrent.futures.ProcessPoolExecutorдля попыток основной интерполяции.
Порядок и правила обработки:
- Загрузка каждого фрагмента из списка
fragment_idsвыполняется через переданныйio_poolвызовомfetch(fragment_id). - После успешной загрузки фрагмента запустите основную интерполяцию в
cpu_pool: вызовите соответствующую функцию изinterpolators, передав ей полученный объект фрагмента. - Если
Futureиз пула процессов завершился успехом, сохраните вычисленное значение (value), установивfallback=Falseиerror=None. - Если
Futureзавершился ошибкой, проверьте причину с помощью предоставленной функцииis_submission_serialization_failure(future):
- При подтверждении сбоя сериализации задачи или аргументов запустите для этого фрагмента функцию
backup(fragment)в пуле потоковio_pool. В результат запишите полученное значение резервного расчета, флагfallback=Trueиerror=None. - Если проверка не подтвердила сбой сериализации (включая любые вычислительные исключения внутри самого алгоритма, такие как
AttributeError,ValueErrorили даже возбуждённый внутри тела функцииPicklingError), резервный расчет не запускается. В результат запишитеvalue=None,fallback=False, а в полеerrorсохраните исходный объект исключения изFutureпула процессов.
- Обработайте все переданные идентификаторы. Завершать или закрывать переданные пулы
io_poolиcpu_pool(вызыватьshutdown) запрещено: пулы принадлежат вызывающей стороне.
Функция должна вернуть словарь, сопоставляющий каждый идентификатор из fragment_ids с итоговым словарем вида:
{'value': ..., 'fallback': ..., 'error': ...}.
Порядок ключей в словаре не имеет значения. Если передан пустой список идентификаторов, верните пустой словарь {}.
Ввод и вывод
Стандартные потоки ввода stdin и вывода stdout в решении не используются: считывать ввод и вызывать print() не нужно. После выполнения кода учащегося проверяющая система считывает из пространства имён модуля функцию верхнего уровня build_height_map и вызывает её с проверочными аргументами.
Пример использования
def make_local_interpolator():
def interpolate(fragment):
return sum(value * value for value in fragment['values'])
return interpolate
specifications = {10: {'values': [2, 3]}, 20: {'values': [2, 3]}}
with make_fallback_example_case(specifications) as case:
interpolators = {10: case.interpolate, 20: make_local_interpolator()}
result = build_height_map([10, 20], case.fetch, interpolators, case.backup,
case.io_pool, case.cpu_pool)
for fragment_id in [10, 20]:
row = result[fragment_id]
print(fragment_id, row['value'], row['fallback'], row['error'])
Вывод:
10 13 False None 20 5 True None
Для фрагмента 10 функция case.interpolate успешно выполняется в пуле процессов: сумма квадратов значений [2, 3] даёт 13, резервный расчёт не требуется (fallback=False), ошибок нет (error=None). Для фрагмента 20 передана локальная функция make_local_interpolator(), которая не может быть сериализована для пула процессов. Диагностика подтверждает ошибку сериализации вызова, поэтому конвейер выполняет резервную функцию case.backup в пуле потоков, вычисляя сумму 2 + 3 = 5, отмечая срабатывание резерва (fallback=True) и завершая обработку без ошибки (error=None).
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Связанные задачи
- ProcessPoolExecutor и lambda: когда возникает ошибка pickle
- ProcessPoolExecutor Python: обработка результатов и ошибок сериализации
- Concurrent.futures Python: первые K успешных результатов двух пулов
- Потоки и процессы Python: аварийная остановка двухэтапного конвейера
- Concurrent.futures Python: выбор исполнителя по размеру пакета
- Потоки и процессы Python: двухэтапное резервное копирование
