一些实验(观看与系统工具的交互会话)表明,在没有额外用户命令的情况下,Tcl 应用程序进程中的每个 Tcl 解释器占用 300kB 到 350kB 之间的某个位置。用户命令和脚本是额外的,堆栈帧也是如此(在解释器中运行任何东西所必需的)。相乘,你可能会得到 17MB 的 50 个解释器上下文,任何现代计算机都可以处理而不会跳过一个节拍。请注意,口译员不允许同时执行。
线程更重,因为 Tcl 的线程模型让每个线程都有自己的主解释器(事实上,所有解释器都严格绑定到单个线程,这种技术用于在 Tcl 的实现中大大减少全局锁的数量)。因此,推荐的线程数很大程度上取决于部署硬件中可用 CPU 的数量以及代码受 CPU 限制而不是 IO 限制的程度。
如果您可以使用 Tcl 8.6(在我写这篇文章时,8.6.0 被标记为在存储库中发布,但未发布),那么您可以使用协程对连接状态进行建模。它们比口译员要轻得多,并且可以用来执行一种协作式多任务处理:
# Your code, with [co_gets] (defined below) instead of [gets]
proc interaction_body {in out} {
try {
puts -nonewline $out "login: "
co_gets $in login ;# check for EOF
puts -nonewline $out "password: "
co_gets $in password ;# check for EOF
if {![check_login $login $password]} {
# Login failed; go away...
return
}
while {[co_gets $in command] >= 0} {
switch -- $command {
...
}
}
} finally {
close $in
}
}
# A coroutine-aware [gets] equivalent. Doesn't handle the full [gets] syntax
# because I'm lazy and only wrote the critical bits.
proc co_gets {channel varName} {
upvar 1 $varName var
fileevent $channel readable [info coroutine]
while 1 {
set n [gets $channel var]
if {$n >= 0 || ![fblocked $channel]} {
fileevent $channel readable {}
return $n
}
yield
}
}
# Create the coroutine wrapper and set up the channels
proc interaction {sock addr port} {
# Log connection by ${addr}:${port} here?
fconfigure $sock -blocking 0 -buffering none
coroutine interaction_$sock interaction_body $sock $sock
}
# Usual tricks for running a server in Tcl
socket -server interaction 12345; # Hey, that port has the same number as my luggage!
vwait forever
如果您需要进行 CPU 密集型处理并且需要小心保护登录名(考虑使用tls 包来保护与 SSL 的连接),这不适合。