Если вы не используете Tk, основные причины использования цикла событий предназначены для ожидания в фоновом режиме, когда вы выполняете какую-либо другую задачу, связанную с I/O, и для обработки серверных сокетов.
Давайте рассмотрим серверные сокеты.
При открытии сервера сокета с:
socket -server myCallbackProcedure 12345
Вы устраивая для обработчика событий, чтобы быть установлен на сокете сервера, так что, когда есть входящее соединение, что соединение превращает вас нормальное socket и предлагаемая вами процедура обратного вызова (myCallbackProcedure
) вызывается для взаимодействия с сокетом. Это часто делается путем установки обработчика fileevent
, так что поступающие данные обрабатываются, когда они поступают, а не блокируют ожидающий его процесс, но это необязательно.
Контур события? Это фрагмент кода, который вызывает в ОС (через select()
, poll()
, WaitForMultipleObject()
и т. Д., В зависимости от ОС и вариантов сборки), чтобы ждать, пока что-то не произойдет на любом назначенном канале или не произойдет тайм-аут. Это очень эффективно, так как поток, вызывающий вызов, может быть приостановлен во время ожидания. Если что-то происходит, вызов ОС возвращается, а Tcl устраивает соответствующие вызовы. (Есть внутренняя очередь.) Это цикл, потому что, как только события обрабатываются, нормально возвращаться и ждать еще немного. (Это то, что делает Tk, до тех пор, пока нет никаких окон для его контроля, и что делает vwait
до тех пор, пока переменная, которую он ждет, не задана каким-либо обработчиком событий.)
Асинхронные ожидания управляются с помощью упорядоченной по времени очереди, и перевести на установку тайм-аута на вызов в ОС.
Пример:
socket -server myCallbackProcedure 12345
proc myCallbackProcedure {channel clientHost clientPort} {
puts "Connection from $clientHost"
puts $channel "Hi there!"
flush $channel
close $channel
}
vwait forever
# The “forever” is an idiom; it's just a variable that isn't used elsewhere
# and so is never set, and it indicates that we're going to run the process
# until we kill it manually.
Несколько более сложный пример с обработкой асинхронного соединения таким образом, мы можем обслуживать несколько соединений одновременно (ЦП необходимо: минимальное):
socket -server myCallbackProcedure 12345
proc myCallbackProcedure {channel clientHost clientPort} {
puts "Connection from $clientHost"
fileevent $channel readable [list incoming $channel $clientHost]
fconfigure $channel -blocking 0 -buffering line
puts $channel "Hi there!"
}
proc incoming {channel host timeout} {
if {[gets $channel line] >= 0} {
puts $channel "You said '$line'"
} elseif {[eof $channel]} {
puts "$host has gone"
close $channel
}
}
vwait forever
Еще более сложный пример который закроет соединения через 10 секунд (= 10000 мс) после последнего сообщения о них:
socket -server myCallbackProcedure 12345
proc myCallbackProcedure {channel clientHost clientPort} {
global timeouts
puts "Connection from $clientHost"
set timeouts($channel) [after 10000 [list timeout $channel $clientHost]]
fileevent $channel readable [list incoming $channel $clientHost]
fconfigure $channel -blocking 0 -buffering line
puts $channel "Hi there!"
}
proc incoming {channel host timeout} {
global timeouts
after cancel $timeouts($channel)
if {[gets $channel line] >= 0} {
puts $channel "You said '$line'"
} elseif {[eof $channel]} {
puts "$host has gone"
close $channel
unset timeouts($channel)
return
}
# Reset the timeout
set timeouts($channel) [after 10000 [list timeout $channel $host]]
}
proc timeout {channel host} {
global timeouts
puts "Timeout for $host, closing anyway..."
close $channel
unset -nocomplain timeouts($channel)
}
vwait forever