Отображение индикатора выполнения tqdm при использовании многопроцессорной обработки Python

Я пытаюсь выполнить некоторую сложную с вычислительной точки зрения задачу с использованием библиотеки Python multiprocessing, и я хотел бы показать индикатор выполнения tqdm для каждого рабочего. В частности, я бы предпочел иметь эту функцию для multiprocessing.Process рабочих или multiprocessing.Pool рабочих.

Мне известны аналогичные вопросы StackOverflow по этой теме (см., Например, (1) Многопроцессорность: используйте tqdm для отображения индикатора выполнения, (2) Показать ход выполнения вызова imap_unordered для многопроцессорного пула Python?, (3) индикатор выполнения tqdm и многопроцессорность), но все они, похоже, заинтересованы в том, чтобы показать одну полосу выполнения для всех рабочих процессов. Я хотел бы показать индикатор выполнения для каждого рабочего.

Вот пример функции, заменяющей мою дорогостоящую в вычислительном отношении функцию, которую я хотел бы использовать в нескольких процессах:

from tqdm import notebook
import time
def foo2(id):
    total = 100
    with notebook.tqdm(total=total, position=id) as pbar:
        for _ in range(0, total, 5):
            pbar.update(5)
            time.sleep(0.1)

Когда я пробую это последовательно, я получаю ожидаемые результаты: 5 индикаторов выполнения заполняются одна за другой.

Однако, когда я пытаюсь сделать это с помощью multiprocessing, я получаю желаемое ускорение, но индикаторы выполнения не отображаются. Это верно независимо от того, использую ли я Pool рабочих или Process рабочих. Вот мой пример кода:

%%time
from multiprocessing import Pool
pool = Pool(5)
pool.map(foo2, range(5))
pool.close()
pool.join()

Пул - без индикаторов выполнения

В комментариях здесь (https://github.com/tqdm/tqdm/issues/407#issuecomment-322932800), я попытался использовать несколько ThreadPool рабочих процессов, и это, как ни странно, смогло отображать индикаторы выполнения. Однако в моей ситуации я бы предпочел использовать Pool или Process воркеров с индикаторами выполнения.

%%time
from multiprocessing.pool import ThreadPool
pool = ThreadPool(5)
pool.map(foo2, range(5))
pool.close()
pool.join()

ThreadPool - отображаются индикаторы выполнения!

Надеюсь, кто-нибудь сможет мне с этим помочь. Я перепробовал все, что мог придумать. Для справки я использую Python 3.7.7 и tqdm 4.57.0.


person andytaylor823    schedule 25.02.2021    source источник
comment
позвольте мне понять это, вам нужен 1 процесс для каждого индикатора выполнения, и каждый индикатор выполнения должен отображаться в соответствии с назначенной задачей?   -  person alexzander    schedule 25.02.2021
comment
Да, точно. Я хотел бы запускать N процесс и отображать N индикаторы выполнения, каждая из которых соответствует своему собственному процессу.   -  person andytaylor823    schedule 25.02.2021
comment
вам нужны N + 1 процессы, N для индикаторов выполнения и 1 для sys.stdout, который взаимодействует с другими N процессами, чтобы достичь желаемого. этот single процесс должен всегда проверять the others, чтобы увидеть свои progress, а затем обновлять данные на экране. Я не совсем уверен в том, что то, что я говорю, может быть реализовано, но вы всегда должны быть позитивными и пытаться.   -  person alexzander    schedule 25.02.2021
comment
Спасибо за ваш ответ. Поскольку я предоставил короткие фрагменты кода, я все еще ищу ответ, который мог бы решить мою конкретную ситуацию. Я считаю, что установка этой проблемы довольно проста, но решение все еще ускользает от меня. У меня закончились возможности попробовать, поэтому я разместил здесь свой вопрос.   -  person andytaylor823    schedule 25.02.2021
comment
возможно, это поможет вам: pypi.org/project/tqdm-multiprocess.   -  person alexzander    schedule 26.02.2021


Ответы (1)


Просматривая сообщения о проблемах на главной странице github для tqdm, я нашел хак, который мне подходит, но он определенно кажется хаком, а не настоящим исправлением проблемы: https://github.com/tqdm/tqdm/issues/485#issuecomment-473338308

Новый (рабочий) код выглядит так:

from tqdm import notebook
import time
def foo2(id):
    total = 100
    print(' ', end='', flush=True)
    for _ in notebook.tqdm(range(0, total, 5)):
        time.sleep(0.1)

плюс

%%time
pool = Pool(5)
#pool.map(foo2, range(5)) # this also works fine with the new hack
for i in range(5):
    pool.apply_async(foo2, args=(i,))
pool.close()
pool.join()
person andytaylor823    schedule 26.02.2021