Tcl 中的事件循环
Event loop in Tcl
我是 Tcl 的初学者。我试图在不参与 Tk 的情况下学习 Tcl。我遇到了像 vwait
和 after
这样的命令,但我很困惑,因为大多数解释都涉及 event loop
的概念,并且主要通过使用 Tk 进一步演示了这个概念。我想了解事件和事件循环的概念以及我提到的命令如何与它们相关,请给我一些参考。解释不应该使用 Tk 作为示例,不使用 Tcl 扩展,假设没有事件的先验知识。除了 GUI programming/Tk 之外,一些(最小的)玩具示例和 tcl 事件循环的实际应用将不胜感激。
我在 Tclers wiki 上看到了这个 tutorial。我正在寻找类似这样的其他参考或解释。
如果您不使用 Tk,使用事件循环的主要原因是在执行其他一些 I/O-bound 任务时在后台等待,以及处理服务器套接字。
让我们看看服务器套接字。
当您打开服务器套接字时:
socket -server myCallbackProcedure 12345
您正在安排在服务器套接字上设置一个事件处理程序,以便当有传入连接时,该连接将转换为普通套接字,并且您提供的回调过程 (myCallbackProcedure
) 是调用以处理与套接字的交互。这通常是通过设置 fileevent
处理程序来完成的,以便在传入数据到达时对其进行处理,而不是阻塞等待它的进程,但它不必如此。
事件循环?这是一段调用 OS 的代码(通过 select()
、poll()
、WaitForMultipleObject()
等,取决于 OS 和构建选项)等待直到任何指定的频道发生某些事情或发生超时。这是非常有效的,因为进行调用的线程可以在等待时挂起。如果发生某些事情,OS 调用 returns 并且 Tcl 安排调用相关的回调。 (内部有一个队列。)这是一个循环,因为一旦处理完事件,返回并等待更多是正常的。 (这就是 Tk 所做的,直到没有更多的 windows 可供它控制,vwait
所做的直到它正在等待的变量被某个事件处理程序设置。)
异步等待使用按时间排序的队列进行管理,并将调用超时设置为 OS。
一个例子:
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.
一个稍微复杂一些的异步连接处理示例,因此我们可以同时服务多个连接(需要CPU:最少):
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
我是 Tcl 的初学者。我试图在不参与 Tk 的情况下学习 Tcl。我遇到了像 vwait
和 after
这样的命令,但我很困惑,因为大多数解释都涉及 event loop
的概念,并且主要通过使用 Tk 进一步演示了这个概念。我想了解事件和事件循环的概念以及我提到的命令如何与它们相关,请给我一些参考。解释不应该使用 Tk 作为示例,不使用 Tcl 扩展,假设没有事件的先验知识。除了 GUI programming/Tk 之外,一些(最小的)玩具示例和 tcl 事件循环的实际应用将不胜感激。
我在 Tclers wiki 上看到了这个 tutorial。我正在寻找类似这样的其他参考或解释。
如果您不使用 Tk,使用事件循环的主要原因是在执行其他一些 I/O-bound 任务时在后台等待,以及处理服务器套接字。
让我们看看服务器套接字。
当您打开服务器套接字时:
socket -server myCallbackProcedure 12345
您正在安排在服务器套接字上设置一个事件处理程序,以便当有传入连接时,该连接将转换为普通套接字,并且您提供的回调过程 (myCallbackProcedure
) 是调用以处理与套接字的交互。这通常是通过设置 fileevent
处理程序来完成的,以便在传入数据到达时对其进行处理,而不是阻塞等待它的进程,但它不必如此。
事件循环?这是一段调用 OS 的代码(通过 select()
、poll()
、WaitForMultipleObject()
等,取决于 OS 和构建选项)等待直到任何指定的频道发生某些事情或发生超时。这是非常有效的,因为进行调用的线程可以在等待时挂起。如果发生某些事情,OS 调用 returns 并且 Tcl 安排调用相关的回调。 (内部有一个队列。)这是一个循环,因为一旦处理完事件,返回并等待更多是正常的。 (这就是 Tk 所做的,直到没有更多的 windows 可供它控制,vwait
所做的直到它正在等待的变量被某个事件处理程序设置。)
异步等待使用按时间排序的队列进行管理,并将调用超时设置为 OS。
一个例子:
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.
一个稍微复杂一些的异步连接处理示例,因此我们可以同时服务多个连接(需要CPU:最少):
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