¿Es seguro usar un Intvar / DoubleVar en un hilo de Python?

Permítanme comenzar con esto diciendo que casi sin duda Queue una Queue para mi progtwig. Estoy publicando esta pregunta para satisfacer más o menos mi curiosidad después de pasar una cantidad decente de tiempo investigando este tema sin encontrar ninguna respuesta concluyente.

Entonces, la pregunta: ¿es seguro acceder / editar un IntVar() , DoubleVar() , etc., desde cualquier lugar que no sea el bucle principal? Además, ¿qué hay de simplemente leer el valor (a través de x.get() ) de un hilo separado? Sé que uno no debe editar / actualizar widgets desde hilos separados, pero no he encontrado información sobre Intvars y similares. Cualquier idea sería muy apreciada.

Aquí hay una pregunta relacionada (pero bastante antigua) que nunca fue contestada realmente:

Python / Tkinter: ¿Son seguros los hilos Tkinter StringVar (IntVar, etc)?

Según los comentarios en el código fuente del módulo _tkinter , parece que tkinter en realidad está destinado a ser seguro para subprocesos, siempre que Tcl se haya creado con la opción --enable-threads . Esto está respaldado por un error resuelto en el rastreador de Python (número 11077 ) que indica que tkinter no es seguro para subprocesos, donde finalmente se determinó que todos los problemas de seguridad de subprocesos con tkinter eran errores corregidos en Python 2.7.3+

Aquí está lo que _tkinter la fuente del módulo _tkinter sobre el problema:

El intérprete de Tcl solo es válido en el subproceso que lo creó, y toda la actividad de Tk también debe ocurrir en este subproceso. Eso significa que mainloop debe invocarse en el hilo que creó el intérprete. Es posible invocar comandos desde otros hilos; _tkinter pondrá en cola un evento para el subproceso del intérprete, que luego ejecutará el comando y devolverá el resultado. Si el subproceso principal no está en el mainloop, e invocar comandos provoca una excepción; Si el bucle principal se está ejecutando pero no está procesando eventos, la invocación del comando se bloqueará.

Entonces, mientras el mainloop se ejecute activamente en el subproceso principal de la aplicación, tkinter progtwigrá el método para que se ejecute automáticamente en el subproceso principal, lo que lo haría seguro para subprocesos. Dicho esto, la mayoría de las fonts en Internet, aparte del código fuente real de Tkinter y el informe de error anterior, indican que el uso de tkinter con hilos está invitando a un locking. No estoy seguro de qué creer, aunque en algunos ejemplos pequeños lo intenté, actualizar la GUI desde un hilo funcionó bien.

Ahora, se preguntaba específicamente si las reglas de seguridad de subprocesos relacionadas con los widgets de Tk se aplicaban también a las subclases de Variable . Lo hace: aquí está algo de la implementación de Variable , el padre de 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) 

Cuando set una variable, llama al método globalsetvar en el widget maestro asociado con la Variable . El método _tk.globalsetvar se implementa en C , y llama internamente a var_invoke , que internamente llama a WaitForMainLoop , que intentará progtwigr el comando para su ejecución en el hilo principal, como se describe en la cita de la fuente _tkinter que _tkinter anteriormente.

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

Tenga en cuenta que esta ruta de código también se usa para obtener operaciones, por lo que las operaciones de set y get se rigen por las mismas reglas.