在Python线程中使用Intvar/DoubleVar是否安全? [英] Is it safe to use a Intvar/DoubleVar in a Python thread?

查看:432
本文介绍了在Python线程中使用Intvar/DoubleVar是否安全?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

让我开始说这句话,我无疑会在程序中使用Queue.我花了相当长的时间研究此主题而没有找到任何确定性的答案之后,我或多或少地满足了我的好奇心.

Let me just preface this by saying, I will almost undoubtedly use a Queue for my program. I'm posting this question to more or less just satisfy my curiosity after spending a decent amount of time researching this topic without finding any conclusive answers.

那么,问题是:从主循环以外的任何地方访问/编辑IntVar()DoubleVar()等是否安全?此外,从一个单独的线程中简单地读取值(通过x.get())怎么样?我知道不应该从单独的线程编辑/更新窗口小部件,但是我没有找到有关Intvars等的信息.任何见识将不胜感激.

So, the question: Is it safe to access/edit an IntVar(), DoubleVar() etc, from anywhere other than the main loop? Also, what about simply reading the value (via x.get() ) from a separate thread? I know one should not edit/update widgets from separate threads, but I have found no information regarding Intvars and the like. Any insight would be greatly appreciated.

这是一个相关的(但相当古老)的问题,从未得到真正的回答:

Here's a related (but fairly old) question that was never really answered:

Python/Tkinter:是Tkinter StringVar(IntVar等) )线程安全吗?

推荐答案

基于 _tkinter模块在源代码中的注释,看来tkinter实际上至少是打算具有线程安全性,只要Tcl是使用--enable-threads选项.这得到了Python跟踪器上已解决的错误( issue11077 )的支持,该错误指出tkinter不是线程-安全,最终确定tkinter的所有线程安全问题都是Python 2.7.3+中修复的错误.

Based on comments in the source code for the _tkinter module, it seems like tkinter actually is at least intended to be thread-safe, as long as Tcl was built with the --enable-threads option. This is backed up by a resolved bug on the Python tracker (issue11077) stating that tkinter isn't thread-safe, where it was eventually determined that all thread-safety issues with tkinter were bugs that were fixed in Python 2.7.3+

这是_tkinter模块来源针对此问题说的话:

Here is what the _tkinter module's source says on the issue:

Tcl解释器仅在创建它的线程中有效,并且所有Tk活动也必须在该线程中发生.这意味着必须在创建解释器的线程中调用mainloop. 可以从其他线程调用命令; _tkinter将为解释器线程排队一个事件,然后该事件将执行命令并传回结果.如果主线程不在mainloop中,则调用命令会导致异常.如果主循环正在运行但未处理事件,则命令调用将阻塞.

The Tcl interpreter is only valid in the thread that created it, and all Tk activity must happen in this thread, also. That means that the mainloop must be invoked in the thread that created the interpreter. Invoking commands from other threads is possible; _tkinter will queue an event for the interpreter thread, which will then execute the command and pass back the result. If the main thread is not in the mainloop, and invoking commands causes an exception; if the main loop is running but not processing events, the command invocation will block.

因此,只要mainloop在应用程序的主线程中处于活动状态,tkinter就会自动安排该方法在主线程中运行,这将使其成为线程安全的.也就是说,除了实际的Tkinter源代码和上面的错误报告之外,大多数Internet上的资源都表明,将tkinter与线程一起使用会导致崩溃.我不太确定该相信什么,尽管在我尝试过的一些小例子中,从线程更新GUI效果很好.

So, as long as the mainloop is actively running in the main thread of the application, tkinter will schedule the method to run in the main thread automatically, which would make it thread-safe. That said, most sources out on the internet, aside from the actual Tkinter source code and the above bug report, indicate that using tkinter with threads is inviting a crash. I'm not quite sure what to believe, though in some small examples I tried, updating the GUI from a thread worked fine.

现在,您特别想知道与Tk小部件相关的线程安全规则是否也适用于Variable子类.它的作用是:这是IntVar的父级Variable的一些实现:

Now, you were specifically wondering if the thread-safety rules related to Tk widgets applied to Variable sub-classes, too. It does: Here's some of the implementation of Variable, the parent of 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方法用C 实现,并且内部调用var_invoke,内部调用WaitForMainLoop,这将尝试调度命令在主线程中执行,如上面引用的_tkinter源中的引言所述.

When you set a variable, it calls the globalsetvar method on the master widget associated with the Variable. The _tk.globalsetvar method is implemented in C, and internally calls var_invoke, which internally calls WaitForMainLoop, which will attempt schedule the command for execution in the main thread, as described in the quote from the _tkinter source I included above.

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);
}

请注意,此代码路径也用于get操作,因此setget操作均受相同规则支配.

Note that this code path is used for get operations, too, so both set and get operations are governed by the same rules.

这篇关于在Python线程中使用Intvar/DoubleVar是否安全?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆