10
2
2014
2

Tcl 现代方法:协程(coroutine)

协程已经是现代语言的必备要素了。Tcl 的协程仿效的是 Lua,在 8.6 版本中引入。(感谢 NRE 引擎!)

 

协程者,何也?说来简单,实际上也不简单。怎么办呢?最简单的理解办法,就是自己写写用用。协程用最简单的语言说,就是临时交出流程控制权,有点像 setjmp/longjmp。例如在服务器中,一个单线程单进程的服务器,给每个客户打开套接字,然后在上面等待套接字可读,如果可读,则控制权交给处理器,处理器处理后 yield 回控制权。有点像这样的场景:你是客服,用户打电话进来,你就把他们的要求提交给技术人员,他们做完事情后给你答复,你就可以再把答复给用户。但如果技术人员一天不答复(不 yield),你就只好在那里等着了。Tcl 中的协程如果不 yield,但是结束完毕 return 了,那么这个结果会返回给调用者,好比技术人员累死了,你就只好把他的遗言告诉用户了……

协程不仅仅在这种场景下有用,它可以用在任何类似这种场景的地方:前台 + 后台,后台按需调用。而协程尤其在 GUI 编程中好用:不用打破事件循环,不用麻烦的线程,轻松处理一些本来就应该是同步处理的任务。

我给个简单的例子吧。

package require Tk
namespace import tcl::mathop::*

wm title . "demo for coroutine"

for {set i 0} {$i < 5} {incr i} {
    pack [entry .e$i]
}
pack [button .btn -text 检验 -command check]

coroutine nextValue apply {{} {
    yield
    for {set i 0} {$i < 5} {incr i} {
        if {[.e$i get] eq ""} {
            yield "第 [+ $i 1] 栏是空的"
        } else {
            yield "第 [+ $i 1] 栏填了:[.e$i get]"
        }
    }
    yield finished
}}

proc check {} {
    while {[set cur [nextValue]] ne "finished"} {
        tk_messageBox -message $cur
    }
    rename nextValue {} ;# 删除 coroutine
    proc nextValue {} {
        tk_messageBox -message "协程已经被删除了噢~"
        return finished
    }
}

注意,coroutine 中第一个 yield 是必不可少的。

当然,其实这个例子完全可以不用协程,只在 check 里面写判断代码,但是我们想一想,如果我们需要进一步扩充程序功能,那样 check 会变得非常庞大,而使用协程的话,我们可以把产生协程的代码放在提供功能的地方,而 check 处完全无需任何改动!是不是很棒呢~在 MemMaster 中,协程就用在了答案检查中,因为 MemMaster 会支持多种多样的检查模式,如有序完全输入、无序部分输入、思维导图模式等等,使用协程可以使代码更易于维护,更可读,更好扩展,也能减少很多代码量。

最后多添一笔,虽然 Tcl 的协程仿效 Lua,但 Tcl 的更好用,[co args] 比 co.resume(args),yield 比 coroutine.yield,很明显 Tcl 的更短。另外 Tcl 还有个叫 yieldto 的功能,直接把控制权交给另外的东西(过程或协程)。

Category: 编程 | Tags: tcl coroutine tcl 现代方法 | Read Count: 2957
Avatar_small
依云 说:
Oct 07, 2014 09:07:30 PM

又和 Python 的好像!不过 Python 的 generator 不能指定返回给谁。

Avatar_small
依云 说:
Oct 07, 2014 09:14:49 PM

又和 Python 的好像!不过 Python 的 generator 不能指定返回给谁。


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter

Powered by Chito | Hosted at is-Programmer | Theme: Aeros 2.0 by TheBuckmaker.com