为什么从工作线程更新tkinter控件似乎有效?

2 投票
1 回答
2299 浏览
提问于 2025-04-18 07:39

我在研究tkinter多线程的时候,发现大家都提到tkinter必须在主线程中运行(就像很多图形界面框架一样),而当一个单独的线程需要和图形界面沟通时,必须使用队列,而不是直接从工作线程访问界面组件

我想学习使用队列的方法,但当然一开始我想看看如果我做错了会发生什么,所以我写了这段代码,用来近似计算圆周率(π),运行了几秒钟:

import tkinter as tk
from tkinter import ttk
from threading import Thread
import math

window = tk.Tk()
window.geometry("300x150")

lbl = tk.Label(window, text="Press Start")
lbl.place(width=280, height=60, x=10, y=10)

pb = ttk.Progressbar(window)
pb.place(width=280, height=25, x=10, y=80)

def calculate():
    """ 1 / i^2 =  PI^2 / 6 """
    s = 0.0
    for i in range(1, 10000001):
        s += (1 / i**2)
        if i % 1000000 == 0:            
            value = math.sqrt(s * 6)
            lbl.config(text=value) #???
            pb.step(10) #???

def start():
    lbl.config(text="Press Start")
    #calculate() #irresponsive GUI this way, obviously
    t = Thread(target=calculate)
    t.start()

btn = tk.Button(window, text="Start", command=start)
btn.place(width=280, height=25, x=10, y=115)

window.mainloop()

根据我的理解,标记为???的行是问题所在。但这段代码运行得很好。界面保持响应,标签和进度条都在更新。

这种跨线程错误是不可预测的吗?有时候会发生,有时候又不会?这段代码最终会出问题吗?还是说我太天真了,认为唯一好的解决方案就是使用队列?我觉得在C#中这会导致cross-thread operation not valid的错误,但在Python中似乎没有这个问题。

1 个回答

2

在普通的消费类应用程序中,有一件事情是绝对不能接受的:比如你点击了“开始”按钮,然后在进度条完成之前就关闭了窗口。这种情况下,如果一个大型应用程序的不同部分相互依赖,关闭正在被其他线程使用的界面元素,可能会导致一些不好的后果。我发现,在终端运行这个程序时,有时候它会在意外退出时卡得很厉害,甚至按 ctrl-c 也没有用。

顺便提一下,

s += (1 / i ** 2)

这一行代码是在进行整数除法,所以 s 的值只会改变一次。

撰写回答