Continuation(First-class continuation)是控制指令执行顺序的一种能力。可以用来从当前执行函数跳转回上层调用函数,或者跳转到之前以退出的函数。可以把它想象成将程序的当前状态保存下来,以便以后恢复(有点像给VM做snapshot)。但要注意的是,真正的Continuation仅仅存储执行上下文,而非程序的数据。下面是的比喻能够很好的解释这一点:
话说你在厨房的冰箱前,考虑来点三明治尝尝。如果这时候你做了个continuation,并将其存放到你的口袋中。然后你把火鸡和面包从冰箱里取出来拿到餐台,并把他们做成了你想要的三明治. 此时,如果你把你的continuation从口袋中取出来,并且调用一次的话。你会发现你突然又身处冰箱前, 考虑来点三明治尝尝. 不过,幸运的是, 此时餐台上已经有了个你想要的三明治。而火鸡和面包都不见了. 那么,你就可以径直去把它吃掉了。 :-) (有点像月光宝盒吧?)
Scheme 通过"catch" 以及 call/cc支持Continuation。Common Lisp虽然在标准中没有支持Continuation,但是通过cl-cont可以使用Delimited Continuation。(支持closure以及proper tail recursion的语言都可以实现continuation-passing style,从而实现Continuation。)
Continuation可以用来实现一些常用的设计模式,比如Coroutines (又叫做Green Thread),Exception handling等。以下给出用Scheme编写的演示代码:
1. 如何使用Continuation:
=========================================
(define the-continuation #f)
(define (test)
(let ((i 0))
; call/cc 调用它的第一个函数参数, 将一个代表当前程序
; 执行点的“continuation变量”传递给函数参数。
;
; 在这个例子中,lambda函数将continuation变量k
; 赋值给变量 the-continuation.
;
(call/cc (lambda (k) (set! the-continuation k)))
;
; 每当continuation执行的时候,我们都会回到此处执行下面的代码.
(set! i (+ i 1))
i))
=========================================
上面的代码定义了一个函数test,以及设置了the-continuation作为程序执行点的保存。
下面看一下Continuation是如何工作的:
> (test) ;调用函数test
1
> (the-continuation) ;恢复到保存点继续执行
2
> (the-continuation) ;再一次恢复到保存点继续执行
3
> (define another-continuation the-continuation) ;将continuation保存到另一个变量中
> (test) ;调用函数test, the-continuation中将保存新的程序恢复点。
1
> (the-continuation) ;调用恢复到新的保存点继续执行
2
> (another-continuation) ;恢复到旧的保存点继续执行
4
2. 通过Continuation实现Coroutines
========================================
;;; 线程调度队列.
;;; 保存一个等待执行的线程的程序执行点(continuation)列表.
(define *queue* '())
(define (empty-queue?)
(null? *queue*))
(define (enqueue x)
(set! *queue* (append *queue* (list x))))
(define (dequeue)
(let ((x (car *queue*)))
(set! *queue* (cdr *queue*))
x))
;;; 运行一个新的线程 (proc).
(define (fork proc)
(call/cc
(lambda (k)
(enqueue k) ;将(proc)执行前的点作为continuation保存到队列*queue*中
(proc)))) ;然后开始执行函数(proc)
;;; 将运行权交给队列中的其他线程,自己进入等待队列
(define (yield)
(call/cc
(lambda (k) ;保存放弃运行权时本线程所在的执行点(continuation)到*queue*中。
(enqueue k)
((dequeue))))) ;调用队列中第一个continuation,将执行跳转到那个continuation所保存的执行点,运行权交出。
;;; 退出当前线程,如果所有线程以退出,则退出进程
(define (thread-exit)
(if (empty-queue?)
(exit)
((dequeue))))
========================================
以上代码定义了一个通过Continuation实现的Coroutine的线程调度
下面看看它是如何工作的:
;;; scheme中典型的thread函数的定义:
(define (do-stuff-n-print str)
(lambda ()
(let loop ((n 0))
(format #t "~A ~A/n" str n)
(yield) ;每输出一次str,就将运行权交给其他线程
(loop (1+ n)))))
;;; 创建并运行两个线程.
(fork (do-stuff-n-print "This is AAA"))
(fork (do-stuff-n-print "Hello from BBB"))
(thread-exit)
以下是程序执行结果:
This is AAA 0
Hello from BBB 0
This is AAA 1
Hello from BBB 1
This is AAA 2
Hello from BBB 2
...
参考资料:
http://www.scheme.com/tspl3/intro.html
http://en.wikipedia.org/wiki/Continuation
http://common-lisp.net/project/cl-cont/