Отставание индикатора выполнения с интенсивной вычислительной функцией на отдельном потоке tkinter - PullRequest
1 голос
/ 22 января 2020

Я пытаюсь реализовать GUI, чтобы помочь некоторым коллегам в обработке изображений, но у меня возникла проблема с индикатором выполнения Tkinter (ttk), который продолжает отставать, пока я выполняю работу в другом потоке.

По сути, я хочу, чтобы индикатор выполнения работал в неопределенном режиме и подпрыгивал назад и вперед, как своего рода визуальное подтверждение того, что все еще продолжается (например, «рабочий» круг в windows).

Я настроил его следующим образом:

# Frame/root set up (etc.) above here...

pbv = IntVar()
progressBar = ttk.Progressbar(consoleFrame, orient="horizontal", length=100, variable=pbv, mode="indeterminate")
progressBar.grid(column=1, row=1, sticky=N)
progressBar.start()

root.mainloop()

, и он подпрыгивает, как и предполагалось, и прекрасно работает на основном потоке. Пока все хорошо.

Затем я инициализирую отдельный поток и запускаю очень вычислительную функцию.

externalFunctionThread = threading.Thread(target=expensiveFunctionThread, args=[foo])
externalFunctionThread.deamon = True
externalFunctionThread.start()

С некоторой функцией:

def expensiveFunctionThread(foo):
   for i in range(a_few_iterations):
      superDuperExpensiveFunction(i, foo)
      # Note: Were I to comment this out and replace it with a time.sleep(n), the bar would progress normally
      # Even a for loop that prints numbers up to some large integer would not cause lag

Для контекста, superDuperExaciousFunction - это вызов функции пирадиомики, которая генерирует множество текстурных функций из некоторого изображения.

В результате происходит значительное отставание панели, которая часто блокируется (не двигается), пока работает функция superDuperExoyFunction. двигаться после каждой итерации. Как только поток супердупер дорогой функции завершится, он вернется к нормальному состоянию.

Я смотрел другие потоки на этой плате ( Как подключить индикатор выполнения к функции? , Tkinter: Как использовать потоки, чтобы предотвратить «зависание» главного события l oop , ttk, индикатор зависания , et c.), Но ни один из них не помогает, поскольку это касается с прогресс-баром, замораживающимся в начале, в отличие от отставания в его обновлениях. Последний не работает (в том числе progressBar.update () / progressBar.update_idletasks () ничего не делает). Я боюсь, что это связано с тем, как Python обрабатывает потоки (поскольку в конечном итоге есть только 1 «реальный» поток, который может выполнять работу одновременно, и Python должен циклически обрабатывать, какой поток может работать ( GIL или что-то?)).

В любом случае, есть ли обходной путь для этого? Почему это может происходить?

1 Ответ

0 голосов
/ 24 января 2020

Ладно, после некоторых исследований я обнаружил, что у пирадиомики есть флаг, запрещающий этот вид быстрого переключения задач, и поэтому многопоточность не является жизнеспособным решением. Чтобы решить проблему, как рекомендовано фурами, я реализовал многопроцессорность (на самом деле пул!). Это не только позволило мне запускать индикатор выполнения без каких-либо задержек, но также позволило разделить вычисления по ядрам, что значительно ускорило мою реализацию. Синтаксис очень похож на синтаксис многопоточности, с одним главным исключением, которое вызвало у меня много горя, прежде чем я понял это. Переменные не наследуются дочерними процессами! Следовательно, если я хочу получить значение из Entry или чего-то еще, мне придется явно передать его в дочерний процесс. После этого изменений не так уж и много. Вместо многопоточности просто используйте связанную вещь процесса (Process вместо Thread, multiprocessing.Queue вместо queue). Никаких задержек и большей эффективности!

...