Запустить параллельный расчет в python с обновлением пользовательского интерфейса - PullRequest
0 голосов
/ 17 июня 2020

Извините, если я задаю глупые вопросы, но я не Python специалист по параллельным вычислениям и мне нужна помощь с этой задачей. Я прочитал несколько руководств и погуглил, но не нашел хорошего решения ...

Мне нужно обработать список объектов данных и получить список результатов (порядок должен быть сохранен). Каждое значение можно вычислить независимо, поэтому случай идеально подходит для параллельных вычислений. В списке обычно несколько сотен объектов, а вычисление одного значения занимает несколько минут. Таким образом, прогресс должен отображаться в главном окне (TKinter). Также необходимо отметить, что основной процесс (который обслуживает окно) потребляет некоторую нетривиальную память (в зависимости от случая, но предположим, что 1 ГБ). Однако вычисление значения требует относительно небольшого количества оперативной памяти (несколько десятков мегабайт).

Некоторые из моих мыслей:

  • tqdm не очень хорошо выбор здесь, поскольку мне нужно обновить пользовательский интерфейс окна, а не индикатор выполнения в терминале.

  • Должно быть возможно использовать класс Process напрямую, но проблема выглядит как classi c случай с пулом и изобретение колеса - это неправильно.

  • Я не могу использовать потоки из-за GIL, и мне приходится использовать процессы. Однако я видел некоторую информацию (или, может быть, я что-то неправильно понял), что Pool разветвляет основной процесс (что не очень хорошо, поскольку основной процесс (с окном) использует много оперативной памяти в моем случае). Однако тест показал, что рабочие не потребляют столько же памяти, сколько основной поток. Есть ли что-то, о чем мне нужно беспокоиться?

  • Чтобы иметь возможность показать прогресс, необходимо использовать стратегию asyn c. Это можно сделать, позвонив по номеру pool.apply_async. Но тогда я получу список ApplyResult объектов, и основной процесс должен опросить список с помощью таймера (кажется неправильным), чтобы показать прогресс и проверить полноту. Чтобы этого избежать, следует использовать обратные вызовы. Я видел callback arg (не забывайте также о error_callback) для apply_async:

thread_pool = multiprocessing.Pool(multiprocessing.cpu_count())
for data in datas:
    self.result_list.append(thread_pool.apply_async(calculate_value_func, args = (data, settings), callback = self.update_ui_progress))
thread_pool.close()

Кажется, что приведенный выше код работает, но главное окно должен иметь несколько дополнительных полей, чтобы справиться с этим параллелизмом. Более того, с помощью вышеупомянутой стратегии thread_pool.join следует вызывать где-то еще в коде, что не является хорошим дизайном. И снова мне приходится перебирать список ApplyResult или полагаться на счетчик вызовов обратного вызова, чтобы узнать, когда все задачи завершены. Но работать с общими данными из обратного вызова безопасно, так как он выполняется в основном процессе.

Итак, как правильно решить мою проблему? Есть какое-нибудь изящное решение? Есть ли другие потенциальные проблемы с моим подходом?

Заранее спасибо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...