基于_tkinter 模块的comments in the source code,只要Tcl 是使用--enable-threads 选项构建的,tkinter 似乎实际上至少意图是线程安全的。这得到了 Python 跟踪器 (issue11077) 上已解决的错误的支持,该错误指出 tkinter 不是线程安全的,最终确定 tkinter 的所有线程安全问题都是在 Python 2.7.3 中修复的错误+
_tkinter 模块的消息来源对此问题的看法如下:
Tcl 解释器仅在创建它的线程中有效,并且所有 Tk 活动也必须在此线程中发生。这意味着必须在创建解释器的线程中调用主循环。 可以从其他线程调用命令; _tkinter 将为解释器线程排队一个事件,然后解释器线程将执行命令并将结果传回。 如果主线程不在主循环中,并且调用命令会导致异常;如果主循环正在运行但未处理事件,则命令调用将阻塞。
所以,只要主循环在应用程序的主线程中主动运行,tkinter 就会自动调度该方法在主线程中运行,这将使其成为线程安全的。也就是说,除了实际的 Tkinter 源代码和上述错误报告之外,互联网上的大多数消息来源都表明,将 tkinter 与线程一起使用会导致崩溃。我不太确定该相信什么,尽管在我尝试的一些小示例中,从线程更新 GUI 效果很好。
现在,您特别想知道与 Tk 小部件相关的线程安全规则是否也适用于 Variable 子类。确实如此:这是Variable 的一些实现,IntVar 的父级:
class Variable:
_default = ""
_tk = None
def __init__(self, master=None, value=None, name=None):
"""Construct a variable
MASTER can be given as master widget.
VALUE is an optional value (defaults to "")
NAME is an optional Tcl name (defaults to PY_VARnum).
If NAME matches an existing variable and VALUE is omitted
then the existing value is retained.
"""
# ...snip...
if not master:
master = _default_root
self._master = master
self._tk = master.tk
def set(self, value):
"""Set the variable to VALUE."""
return self._tk.globalsetvar(self._name, value)
当您set 一个变量时,它会在与Variable 关联的主窗口小部件上调用globalsetvar 方法。 _tk.globalsetvar 方法is implemented in C,并在内部调用var_invoke,后者在内部调用WaitForMainLoop,它将尝试安排在主线程中执行的命令,如我在上面包含的_tkinter 源的引用中所述.
static PyObject*
var_invoke(EventFunc func, PyObject *selfptr, PyObject *args, int flags)
{
/* snip */
/* The current thread is not the interpreter thread. Marshal
the call to the interpreter thread, then wait for
completion. */
if (!WaitForMainloop(self))
return NULL;
/* snip */
static PyObject *
Tkapp_GlobalSetVar(PyObject *self, PyObject *args)
{
return var_invoke(SetVar, self, args, TCL_LEAVE_ERR_MSG | TCL_GLOBAL_ONLY);
}
请注意,此代码路径也用于获取操作,因此 set 和 get 操作都受相同规则的约束。