在Python线程中使用Intvar / DoubleVar是否安全?

时间:2014-08-17 17:36:16

标签: python multithreading python-3.x tkinter

让我先说一下,我几乎无疑会使用Queue作为我的计划。我花了相当多的时间研究这个话题而没有找到任何确凿的答案,或多或少只是满足了我的好奇心。

所以,问题是:从主循环以外的任何地方访问/编辑IntVar()DoubleVar()等是否安全?另外,如何从单独的线程中读取值(通过x.get())呢?我知道不应该从单独的线程编辑/更新小部件,但我没有找到有关Intvars等的信息。任何见解将不胜感激。

这是一个从未真正回答的相关(但相当陈旧)的问题:

Python/Tkinter: Are Tkinter StringVar (IntVar, etc) thread safe?

1 个答案:

答案 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操作都受相同规则的约束。