进度条在单独的线程 tkinter 上滞后于计算密集型函数

Progress bar lagging with computationally intensive function on separate thread tkinter

我正在尝试实现一个 GUI 来帮助一些同事进行图像处理,但是我 运行 遇到了 Tkinter (ttk) 进度条的问题,它在我执行工作时一直滞后另一个线程。

基本上,我想让进度条 运行 处于不确定模式并来回弹跳,作为一种视觉确认,表明事情仍在进行中(比如 "working" 中的圆圈 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

对于上下文,superDuperExpensiveFunction 是对 pyradiomics 函数的调用,它从一些图像生成许多纹理特征。

最终发生的事情是,当 superDuperExpensiveFunction 处于 运行ning 状态时,条会严重滞后,经常锁定(不移动),仅在每次迭代后移动。一旦超级 duper 昂贵的函数线程完成,它将 return 恢复正常。

我查看了此板上的其他线程(How to connect a progress bar to a function? , Tkinter: How to use threads to preventing main event loop from "freezing" , 等),但其中 none 有帮助,因为他们担心进度条在开始时冻结,而不是滞后于其更新。最后一个不起作用(包括 progressBar.update()/progressBar.update_idletasks() 什么都不做)。我担心的是它与 Python 处理线程的方式有关(因为最终只有 1 个 "real" 线程可以同时工作,并且 Python 必须循环哪个线程允许工作(GIL 或其他东西?))。

无论如何,是否有解决方法?为什么会发生这种情况?

好的,所以在做了一些研究后我发现 pyradiomics 有一个标志禁止这种快速任务切换,因此线程不是一个可行的解决方案。为了解决这个问题,正如 furas 所建议的那样,我实现了多处理(实际上是池化!)。这不仅让我的进度条 运行 没有任何滞后,而且还能够跨核心划分计算,大大加快了我的实施速度。语法与线程的语法非常相似,有一个主要的例外在我弄清楚之前让我很伤心。变量不会被子进程继承!因此,如果我想从 Entry 或其他东西中获取值,我必须明确地将它传递给子进程。除此之外,真的没有那么多变化。而不是线程,只需使用关联的进程事物(进程而不是线程,multiprocessing.Queue 而不是队列)。没有延迟,效率更高!