Остановка пула процессов Python: Queue, маркеры завершения и join
Напишите решение на Python и проверьте его на странице.
Другие упражнения по этой теме: Multiprocessing и threading Python: задачи на Queue, Lock и остановку.
Остановка пула процессов с общей очередью
В модуле фоновой индексации документов несколько процессов-воркеров разбирают поступающие задачи из одной общей очереди. Когда обработка завершается, диспетчер должен остановить весь пул и дождаться полного завершения каждого процесса.
Для этого была написана функция остановки, однако в ней отправка маркера None и вызов join() объединены в один общий цикл и выполняются попарно для каждого воркера по очереди. Поскольку общая очередь не привязана к конкретным исполнителям, отправленный маркер легко перехватывает произвольный активный процесс, а вовсе не тот, чей join() ожидает диспетчер прямо сейчас. Из-за этого система может зависнуть намертво: диспетчер ждёт завершения процесса, до которого сигнал остановки ещё не дошёл.
Исправьте протокол остановки пула процессов, чтобы исключить взаимную блокировку и гарантировать своевременный выход всех воркеров.
Что уже дано
В редакторе уже содержится исходный код функции stop_process_pool(work_queue, workers), допускающий зависание при конкурентной выборке из очереди.
В среде исполнения также доступны вспомогательные функции для локальной отладки и запуска тестов:
make_pool_stop_probe(worker_count, documents, delayed_first_reader=False)— создаёт тестовый сценарий с реальными процессами и общей очередью;observe_pool_stop(probe)— выполняет функцию остановки на созданном сценарии и возвращает структуру с результатами наблюдений;make_pool_stop_example_observation()— запускает готовый типовой сценарий и возвращает его результаты.
Реализовывать эти вспомогательные функции заново не требуется: они служат для локальной проверки и демонстрации работы.
Что нужно сделать
Исправьте функцию stop_process_pool(work_queue, workers).
Функция принимает два аргумента:
work_queue— реальный экземплярmultiprocessing.Queue, общий для всех запущенных воркеров. В очередь заранее помещено от 0 до 24 документов (произвольные JSON-совместимые значения, кромеNoneверхнего уровня; ложные значения и вложенныеNoneсчитаются обычными документами). Новые задачи в очередь больше не поступают.workers— непустой список из 1–4 уже запущенных процессовmultiprocessing.Process. Каждый процесс в цикле читает документы из общей очереди, а при извлечении первого встреченного значенияNoneверхнего уровня завершает работу.
Требования к поведению функции:
- К моменту возврата из функции каждый из переданных процессов
workersдолжен штатно завершиться. - Каждый документ, находившийся в очереди до начала остановки, должен быть обработан ровно один раз без изменений.
- Значения в очереди не привязаны к конкретным процессам: любой активный воркер может забрать как очередной документ, так и отправленный сигнал остановки.
- Время выполнения функции ограничено бюджетом в 5 секунд на каждый вызов (допускаются безопасные ожидания с таймаутом в пределах этого лимита).
- Возвращаемое значение функции не используется.
Ввод и вывод
Стандартные потоки ввода stdin и вывода stdout в решении не используются: считывать ввод и вызывать print() не требуется. После выполнения кода учащегося проверяющая система считывает из пространства имён модуля функцию верхнего уровня stop_process_pool и вызывает её с проверочными аргументами.
В примере ниже создаётся сценарий с двумя воркерами и двумя документами в общей очереди. Функция observe_pool_stop передаёт очередь и процессы в stop_process_pool, после чего фиксирует состояние системы: флаг all_stopped подтверждает, что оба процесса штатно завершили работу, а processed_count показывает, что оба документа были успешно обработаны.
Пример использования
worker_count = 2
documents = ['draft.txt', 'notes.txt']
probe = make_pool_stop_probe(worker_count, documents)
observed = observe_pool_stop(probe)
print(observed['all_stopped'])
print(observed['processed_count'])
Вывод:
True 2
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Связанные задачи
- Multiprocessing.Queue и join(): причина взаимной блокировки Python
- Multiprocessing Python: ограничение очереди семафором
- Процесс и потоки Python: двухфазная обработка матрицы
- Queue и Lock в Python: дедупликация заявок между рабочими потоками
- Threading.Lock на Python: атомарное изменение общего словаря
- Удаление ключа из ChainMap в Python: проверка поведения del
