Безопасно ли использовать Intvar/DoubleVar в потоке Python?

Позвольте мне просто предсказать это, сказав, что я почти наверняка буду использовать Queue для моей программы. Я публикую этот вопрос, чтобы более или менее просто удовлетворить свое любопытство, потратив приличное количество времени на исследование этой темы, не найдя каких-либо окончательных ответов.

Итак, вопрос: это безопасно для доступа / редактирования IntVar(), DoubleVar() и т. д., кроме как из основного цикла? Кроме того, как насчет простого чтения значения (через x.get()) из отдельной ветки? Я знаю, что не следует редактировать / обновлять виджеты из отдельных потоков, но я не нашел никакой информации относительно Intvars и тому подобного. Любое понимание будет с благодарностью.

Вот связанный (но довольно старый) вопрос, на который так и не был дан ответ:

Python / Tkinter: Является ли Tkinter StringVar (IntVar и т. Д.) Потокобезопасным?

1 ответ

Решение

На основе комментариев в исходном коде для _tkinter Похоже, что модуль tkinter на самом деле предназначен для работы с потоками, если Tcl был собран с --enable-threads вариант. Это подтверждается исправленной ошибкой на трекере Python ( проблема11077), утверждающей, что tkinter не является поточно-ориентированным, и в конечном итоге было установлено, что все проблемы с безопасностью потоков с tkinter были ошибками, которые были исправлены в Python 2.7.3+

Вот что _tkinter Источник модуля говорит по этому вопросу:

Интерпретатор Tcl действителен только в потоке, который его создал, и вся активность Tk также должна происходить в этом потоке. Это означает, что mainloop должен быть вызван в потоке, который создал интерпретатор. Вызов команд из других потоков возможен; _tkinter поставит в очередь событие для потока интерпретатора, который затем выполнит команду и вернет результат. Если основной поток не находится в mainloop, и вызов команд вызывает исключение; если основной цикл выполняется, но не обрабатывает события, вызов команды будет заблокирован.

Таким образом, пока mainloop активно работает в главном потоке приложения, 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 переменная, она вызывает globalsetvar метод на главном виджете, связанном с Variable, _tk.globalsetvar метод реализован на 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);
}

Обратите внимание, что этот путь к коду используется и для операций get, поэтому оба set а также get операции регулируются по тем же правилам.

Другие вопросы по тегам