协程已经是现代语言的必备要素了。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 的功能,直接把控制权交给另外的东西(过程或协程)。
Oct 07, 2014 09:07:30 PM
又和 Python 的好像!不过 Python 的 generator 不能指定返回给谁。
Oct 07, 2014 09:14:49 PM
又和 Python 的好像!不过 Python 的 generator 不能指定返回给谁。