让我先说一下,我几乎无疑会使用Queue
作为我的计划。我花了相当多的时间研究这个话题而没有找到任何确凿的答案,或多或少只是满足了我的好奇心。
所以,问题是:从主循环以外的任何地方访问/编辑IntVar()
,DoubleVar()
等是否安全?另外,如何从单独的线程中读取值(通过x.get()
)呢?我知道不应该从单独的线程编辑/更新小部件,但我没有找到有关Intvars等的信息。任何见解将不胜感激。
这是一个从未真正回答的相关(但相当陈旧)的问题:
Python/Tkinter: Are Tkinter StringVar (IntVar, etc) thread safe?
答案 0 :(得分:4)
基于_tkinter
模块的comments in the source code,看起来tkinter实际上至少预期是线程安全的,只要Tcl是使用{构建的{1}}选项。这是由Python跟踪器(issue11077)上已解决的错误支持的,该错误表明tkinter不是线程安全的,最终确定tkinter的所有线程安全问题都是修复的错误Python 2.7.3 +
以下是--enable-threads
模块的来源在此问题上所说的内容:
Tcl解释器仅在创建它的线程中有效,并且所有Tk活动也必须在此线程中发生。这意味着必须在创建解释器的线程中调用mainloop。 可以从其他线程调用命令; _tkinter将为解释器线程排队一个事件,然后执行该命令并传回结果。如果主线程不在主循环中,则调用命令会导致异常;如果主循环正在运行但未处理事件,则命令调用将阻止。
因此,只要mainloop在应用程序的主线程中主动运行,tkinter就会调度该方法在主线程中自动运行,这将使其成为线程安全的。也就是说,除了实际的Tkinter源代码和上面的错误报告之外,互联网上的大多数消息来源表明,使用带线程的tkinter会引发崩溃。我不太确定该相信什么,不过在我试过的一些小例子中,从一个线程更新GUI工作得很好。
现在,您特别想知道与Tk小部件相关的线程安全规则是否也适用于_tkinter
子类。它确实:以下是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
关联的主窗口小部件上的globalsetvar
方法。 Variable
方法is implemented in C,内部调用_tk.globalsetvar
,内部调用var_invoke
,它将尝试安排命令在主线程中执行,如引用中所述我上面包含的WaitForMainLoop
来源。
_tkinter
请注意,此代码路径也用于get操作,因此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
操作都受相同规则的约束。