Процесс и потоки Python: двухфазная обработка матрицы
Напишите решение на Python и проверьте его на странице.
Другие упражнения по этой теме: Multiprocessing и threading Python: задачи на Queue, Lock и остановку.
Двухфазная нормализация матрицы
Вычислительный конвейер обрабатывает числовые матрицы с помощью выделенного рабочего процесса. Чтобы ускорить масштабирование данных, дочерний процесс распределяет строки матрицы между группой локальных потоков. Вся нормализация разделена на два последовательных шага с промежуточным объединением результатов.
На первом этапе каждый поток подсчитывает сумму значений в назначенных ему строках и под защитой threading.Lock прибавляет её к общему итогу всей матрицы. После этого потоки останавливаются перед барьером, дожидаясь, пока промежуточный подсчёт закончат все остальные участники. Если итоговая сумма матрицы оказывается равной нулю, деление выполнить нельзя: процесс отменяет вторую фазу, отправляет в очередь результатов специальный маркер невозможности масштабирования и сразу завершает работу.
Если же общая сумма отлична от нуля, потоки преодолевают барьер и приступают ко второй фазе: делят элементы своих строк на полученный итог. Дождавшись полного завершения работы всех потоков, процесс отправляет готовую нормализованную матрицу через очередь главному процессу.
Реализуйте функцию двухфазного воркера и диспетчер конвейера, который передаёт матрицу на обработку, считывает из очереди готовый результат или маркер ошибки и корректно завершает рабочий процесс.
Что уже дано
В среде выполнения доступна вспомогательная фабрика make_matrix_workspace(matrix, worker_count) и строковая константа UNSCALABLE = 'UNSCALABLE'. Создаваемый объект рабочего пространства workspace берёт на себя арифметические операции и проверку корректности этапов:
workspace.sum_group(group)— подсчитывает и возвращает сумму строк с индексамиrange(group, len(matrix), worker_count)в текущем потоке (пустая группа даёт сумму0);workspace.add_sum(group, subtotal)— добавляет подсчитанную подсумму к общему итогу; вызов должен выполняться строго под защитой общего объектаthreading.Lock;workspace.total— свойство, возвращающее текущую общую сумму матрицы (доступно после завершения первой фазы);workspace.normalize_group(group)— делит элементы назначенных строк наworkspace.totalв текущем потоке; вызывается только после добавления сумм всеми группами и строго при ненулевом значенииtotal;workspace.normalized_matrix()— возвращает итоговую преобразованную матрицу после того, как все потоки завершат нормализацию.
Также в окружении объявлены функции observe_matrix_probe и make_matrix_example_observation для проверки хода вычислений. В редакторе вам предстоит реализовать функции matrix_worker и run_matrix_pipeline.
Что нужно сделать
Реализуйте две функции: matrix_worker(matrix, worker_count, result_queue) и run_matrix_pipeline(matrix, worker_count).
matrix_worker(matrix, worker_count, result_queue):
- Выполняется в отдельном дочернем процессе
multiprocessing.Process. - Создаёт рабочее пространство через
workspace = make_matrix_workspace(matrix, worker_count). - Создаёт ровно
worker_countнативных потоковthreading.Thread(по одному на каждую группу0 <= group < worker_count) и координирует их с помощью общего объектаthreading.Lockи барьера фаз. - Фаза 1: каждый поток вычисляет подсумму своей группы через
workspace.sum_group(group), захватывает общийthreading.Lockи вызываетworkspace.add_sum(group, subtotal). После добавления поток ожидает завершения первой фазы всеми остальными потоками. - Если итоговая сумма
workspace.total == 0, нормализация полностью пропускается: процесс немедленно отправляет вresult_queueзначениеUNSCALABLEи завершает работу. - Фаза 2: если сумма не равна нулю, те же самые потоки вызывают
workspace.normalize_group(group)для своих групп. После завершения всех потоков воркер отправляет вresult_queueрезультат вызоваworkspace.normalized_matrix().
run_matrix_pipeline(matrix, worker_count):
- Принимает прямоугольную матрицу целых чисел (от 0 до 256 строк, от 0 до 32 столбцов, элементы от -1000 до 1000) и количество потоков
worker_count(от 1 до 4). - Создаёт очередь
multiprocessing.Queueи запускает настоящий дочерний процессmultiprocessing.Process(target=matrix_worker, args=(matrix, worker_count, queue)). - Считывает результат из очереди до ожидания завершения процесса, после чего финализирует процесс вызовом
join()и возвращает полученное значение. - Результатом является нормализованная матрица (числа сверяются с точностью
rel_tol=1e-12,abs_tol=1e-12) либо строкаUNSCALABLE, если сумма элементов матрицы равна нулю.
Ввод и вывод
Стандартные потоки ввода stdin и вывода stdout в решении не используются: считывать ввод и вызывать print() не нужно. После выполнения кода учащегося проверяющая система считывает из пространства имён модуля функции верхнего уровня matrix_worker и run_matrix_pipeline и вызывает их с проверочными аргументами.
Пример использования
observed = make_matrix_example_observation()
print([list(row) for row in observed['result']])
Вывод:
[[0.1, 0.2], [0.3, 0.4]]
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Связанные задачи
- Multiprocessing.Queue и join(): причина взаимной блокировки Python
- Остановка пула процессов Python: Queue, маркеры завершения и join
- Multiprocessing Python: ограничение очереди семафором
- Queue и Lock в Python: дедупликация заявок между рабочими потоками
- Threading.Lock на Python: атомарное изменение общего словаря
- Удаление ключа из ChainMap в Python: проверка поведения del
