我是 Tcl 的初学者。我正在尝试学习 Tcl 而不涉及 Tk。我遇到过像 vwait 和 after 等命令,但我很困惑,因为大多数解释都涉及事件循环的概念,并且进一步主要通过使用来演示这个概念知道。我想了解事件和事件循环的概念以及我提到的命令如何与它们相关,请引用我的一些引用资料。解释不应使用 Tk 作为示例,不使用 Tcl 扩展,假设没有事件的先验知识。除了 GUI 编程/Tk 之外,一些(最小的)玩具示例和 tcl 事件循环的实际应用将受到赞赏。
我遇到了这个tutorial在 Tclers 维基上。我正在寻找类似的其他引用或解释。
最佳答案
如果您不使用 Tk,那么使用事件循环的主要原因是在执行其他一些 I/O 密集型任务时在后台等待,以及处理服务器套接字。
让我们看看服务器套接字。
当您使用以下命令打开服务器套接字时:
socket -server myCallbackProcedure 12345
您正在安排在服务器套接字上设置一个事件处理程序,以便在有传入连接时,该连接将转换为普通套接字和您提供的回调过程 (myCallbackProcedure
)被调用来处理与套接字的交互。这通常是通过设置 fileevent
处理程序来完成的,以便传入数据在到达时得到处理,而不是阻塞等待它的进程,但这不是必须的。
事件循环?这是一段调用操作系统的代码(通过 select()
、poll()
、WaitForMultipleObject()
等,具体取决于操作系统和构建选项)等待任何指定 channel 上发生某些情况或发生超时。它非常高效,因为进行调用的线程可以在等待时挂起。如果发生某些情况,操作系统调用将返回,并且 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.
一个更复杂的异步连接处理示例,因此我们可以一次提供多个连接(所需的 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
关于events - Tcl 中的事件循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28315091/