Spec-Zone.ru › Tcl/Tk

vwait

Имя

vwait — Обработка событий до записи в переменную

Синтаксис

vwait имяПеременной

Описание

Эта команда запускает цикл обработки событий Tcl, блокируя приложение, если события не готовы. Она продолжает обработку событий, пока какой-либо обработчик событий не установит значение глобальной переменной имяПеременной. После того, как имяПеременной будет установлено, команда vwait вернётся, как только обработчик событий, изменивший имяПеременной, завершит работу. Аргумент имяПеременной всегда интерпретируется как имя переменной глобального пространства имен, но может ссылаться на переменные любого пространства имен, если указано полное имя.

В некоторых случаях команда vwait может не вернуть значение немедленно после установки имяПеременной. Это происходит, если обработчик событий, устанавливающий имяПеременной, не завершается немедленно. Например, если обработчик событий устанавливает имяПеременной, а затем сам вызывает vwait для ожидания другой переменной, то он может не вернуться в течение длительного времени. В это время основной vwait блокируется, ожидая завершения обработчика событий, поэтому он также не может вернуть значение. (См. ПРИМЕР ВЛОЖЕННЫХ VWAIT ниже.)

Для ясности, несколько вызовов vwait вложены и не происходят параллельно. Внешний вызов vwait не вернётся, пока все внутренние не вернутся. Рекомендуется, чтобы код никогда не вкладывал вызовы vwait (избегая их размещения в обработчиках событий), но когда это невозможно, следует позаботиться о добавлении переменных блокировки в код, чтобы предотвратить все рекурсивные вызовы vwait, которые не являются строго необходимыми. Имейте в виду, что синхронные режимы работы некоторых пакетов Tcl (например, http) используют vwait внутри; при использовании цикла событий лучше использовать асинхронные режимы работы на основе обратного вызова этих пакетов, если они доступны.

Примеры

Запустить цикл обработки событий непрерывно до тех пор, пока какое-либо событие не вызовет exit. (Вы можете использовать любую переменную, не упомянутую где-либо ещё, но имя forever сразу напоминает о намерениях.)
vwait forever

Подождите пять секунд для подключения к сокету сервера, в противном случае закройте сокет и продолжайте выполнение скрипта:

# Initialise the state
after 5000 set state timeout
set server [socket -server accept 12345]
proc accept {args} {
    global state connectionInfo
    set state accepted
    set connectionInfo $args
}

# Wait for something to happen
vwait state

# Clean up events that could have happened
close $server
after cancel set state timeout

# Do something based on how the vwait finished...
switch $state {
    timeout {
        puts "no connection on port 12345"
    }
    accepted {
       puts "connection: $connectionInfo"
       puts [lindex $connectionInfo 0] "Hello there!"
    }
}

Команда, которая будет ждать некоторого временного задержки, ожидая, что переменная пространства имен будет установлена. Включает блокировку для предотвращения вложенных ожиданий.

namespace eval example {
    variable v done
    proc wait {delay} {
        variable v
        if {$v ne "waiting"} {
            set v waiting
            after $delay [namespace code {set v done}]
            vwait [namespace which -variable v]
        }
        return $v
    }
}

При запуске внутри coroutine альтернативой использованию vwait является yield во внешнем цикле событий и возобновление выполнения, когда переменная устанавливается или в момент бездействия после этого.

coroutine task apply {{} {
    # simulate [after 1000]
    after 1000 [info coroutine]
    yield

    # schedule the setting of a global variable, as normal
    after 2000 {set var 1}

    # simulate [vwait var]
    proc updatedVar {task args} {
        after idle $task
        trace remove variable ::var write "updatedVar $task"
    }
    trace add variable ::var write "updatedVar [info coroutine]"
    yield
}}

Вложенные vwait по примеру

Этот пример демонстрирует, что может произойти, когда команда vwait вложена. Скрипт никогда не завершится, потому что ожидание переменной a никогда не завершается; эта команда vwait всё ещё ждёт завершения скрипта, запланированного с помощью after, который, как раз, выполняет внутренний vwait (для b), даже если событие, которого ожидал внешний vwait (установка a), произошло.
after 500 {
    puts "waiting for b"
    vwait b
    puts "b was set"
}
after 1000 {
    puts "setting a"
    set a 10
}
puts "waiting for a"
vwait a
puts "a was set"
puts "setting b"
set b 42

Если вы запустите вышеуказанный код, вы получите такой вывод:

waiting for a
waiting for b
setting a

Скрипт никогда не выведет «a было установлено», пока не выведет «b было установлено» из-за вложенных команд vwait, но b не будет установлено до возврата внешнего vwait, поэтому скрипт зависнет. Единственные способы избежать этого — это либо структурировать всю программу в стиле продолжения, либо использовать coroutine для создания неявных продолжений. Первый из этих вариантов записывается как:

after 500 {
    puts "waiting for b"
    trace add variable b write {apply {args {
        global a b
        trace remove variable ::b write \
                [lrange [info level 0] 0 1]
        puts "b was set"
        set ::done ok
    }}}
}
after 1000 {
    puts "setting a"
    set a 10
}
puts "waiting for a"
trace add variable a write {apply {args {
    global a b
    trace remove variable a write [lrange [info level 0] 0 1]
    puts "a was set"
    puts "setting b"
    set b 42
}}}
vwait done

Второй вариант, с coroutine и некоторыми вспомогательными процедурами, выполняется следующим образом:

# A coroutine-based wait-for-variable command
proc waitvar globalVar {
    trace add variable ::$globalVar write \
            [list apply {{v c args} {
        trace remove variable $v write \
                [lrange [info level 0] 0 3]
        after 0 $c
    }} ::$globalVar [info coroutine]]
    yield
}
# A coroutine-based wait-for-some-time command
proc waittime ms {
    after $ms [info coroutine]
    yield
}

coroutine task-1 eval {
    puts "waiting for a"
    waitvar a
    puts "a was set"
    puts "setting b"
    set b 42
}
coroutine task-2 eval {
    waittime 500
    puts "waiting for b"
    waitvar b
    puts "b was set"
    set done ok
}
coroutine task-3 eval {
    waittime 1000
    puts "setting a"
    set a 10
}
vwait done

См. также

global, update

Licensed under Tcl/Tk terms
https://www.tcl.tk/man/tcl/TclCmd/vwait.htm

Licensed under Tcl/Tk terms
https://www.tcl.tk/man/tcl/TclCmd/vwait.htm

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API