他们是什么,他们有什么好处?
我没有CS学位,我的背景是VB6 - > ASP - > ASP.NET/C#.任何人都能以清晰简洁的方式解释它吗?
想象一下,如果程序中的每一行都是一个单独的函数.每个接受作为参数的下一行/要执行的函数.
使用此模型,您可以在任何行"暂停"执行并稍后继续执行.您还可以执行创造性操作,例如暂时跳过执行堆栈以检索值,或将当前执行状态保存到数据库以便稍后检索.
你可能比你想象的更好地理解它们.
例外是"仅向上"延续的一个例子.它们允许堆栈内部的代码调用异常处理程序来指示问题.
Python示例:
try: broken_function() except SomeException: # jump to here pass def broken_function(): raise SomeException() # go back up the stack # stuff that won't be evaluated
生成器是"向下"延续的例子.它们允许代码重新进入循环,例如,创建新值.
Python示例:
def sequence_generator(i=1): while True: yield i # "return" this value, and come back here for the next i = i + 1 g = sequence_generator() while True: print g.next()
在这两种情况下,这些都必须特别添加到语言中,而在具有延续的语言中,程序员可以创建这些不可用的东西.
提醒一下,这个例子并不简洁,也不是特别清楚.这是对continuation的强大应用的演示.作为VB/ASP/C#程序员,您可能不熟悉系统堆栈或保存状态的概念,因此这个答案的目标是演示而不是解释.
Continuations非常通用,是一种保存执行状态并在以后恢复的方法.以下是使用Scheme中的continuation的协作多线程环境的一个小例子:
(假设操作在此处未定义的全局队列上按预期排队和出列工作)
(define (fork) (display "forking\n") (call-with-current-continuation (lambda (cc) (enqueue (lambda () (cc #f))) (cc #t)))) (define (context-switch) (display "context switching\n") (call-with-current-continuation (lambda (cc) (enqueue (lambda () (cc 'nothing))) ((dequeue))))) (define (end-process) (display "ending process\n") (let ((proc (dequeue))) (if (eq? proc 'queue-empty) (display "all processes terminated\n") (proc))))
这提供了一个函数可以使用的三个动词 - fork,context-switch和end-process.fork操作分叉线程并在一个实例中返回#t而在另一个实例中返回#f.上下文切换操作在线程之间切换,而end-process终止线程.
以下是它们的使用示例:
(define (test-cs) (display "entering test\n") (cond ((fork) (cond ((fork) (display "process 1\n") (context-switch) (display "process 1 again\n")) (else (display "process 2\n") (end-process) (display "you shouldn't see this (2)")))) (else (cond ((fork) (display "process 3\n") (display "process 3 again\n") (context-switch)) (else (display "process 4\n"))))) (context-switch) (display "ending process\n") (end-process) (display "process ended (should only see this once)\n"))
输出应该是
entering test forking forking process 1 context switching forking process 3 process 3 again context switching process 2 ending process process 1 again context switching process 4 context switching context switching ending process ending process ending process ending process ending process ending process all processes terminated process ended (should only see this once)
那些在课堂上学习分叉和穿线的人经常会给出类似的例子.这篇文章的目的是证明通过延续,你可以在一个线程中通过手动保存和恢复其状态 - 它的延续 - 来获得类似的结果.
PS - 我想我在On Lisp中记得类似的东西,所以如果你想看专业代码,你应该检查一下这本书.
考虑延续的一种方法是作为处理器堆栈.当你"使用current-continuation c调用"时,它调用你的函数"c",传递给"c"的参数是你当前的堆栈,上面包含你所有的自动变量(表示为另一个函数,称之为"k") ").与此同时,处理器开始创建一个新的堆栈.当你调用"k"时,它会在原始堆栈上执行"从子程序返回"(RTS)指令,跳回原来的"call-with-current-continuation"(从现在起"call-cc")的上下文on)并允许您的程序像以前一样继续.如果您将参数传递给"k",那么这将成为"call-cc"的返回值.
从原始堆栈的角度来看,"call-cc"看起来像是一个普通的函数调用.从"c"的角度来看,您的原始堆栈看起来像一个永不返回的函数.
有一个关于数学家的老笑话,他通过爬进笼子,锁定它,并宣称自己在笼子外面,而其他一切(包括狮子)在里面捕获了一只笼子里的狮子.延续有点像笼子,"c"有点像数学家.你的主程序认为"c"在里面,而"c"认为你的主程序在"k"里面.
您可以使用continuation创建任意控制流结构.例如,您可以创建一个线程库."yield"使用"call-cc"将当前的continuation放在队列中,然后跳转到队列头部的那个.信号量也有自己的暂停连续队列,并通过将其从信号量队列中取出并将其放入主队列来重新安排线程.