2008-09-02 48 views
32

它們是什麼,它們有什麼用途?我只是沒有得到延續!

我沒有一個CS學位,我的背景是VB6 - > ASP - > ASP.NET/C#。任何人都可以用清晰簡潔的方式解釋它嗎?

回答

41

試想一下,如果在你的程序每一行是一個單獨的函數。每個接受作爲參數的下一行/函數來執行。

使用這個模型,你可以「暫停」的執行在任何線並在稍後繼續它。你也可以做一些有創意的事情,比如暫時跳出執行堆棧來檢索一個值,或者將當前的執行狀態保存到數據庫中以便稍後檢索。

+0

哇,這實際上是有道理的。謝謝! – minimalpop 2009-12-03 03:02:56

9

擡起頭來,這個例子並不簡明,也非常清楚。這是一個強大的延續應用的演示。作爲VB/ASP/C#程序員,您可能不熟悉系統堆棧或保存狀態的概念,因此此答案的目標僅僅是演示而不是解釋。

延續用途極其廣泛,是一種保存的執行狀態,並在稍後繼續。下面是使用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。上下文切換操作在線程之間切換,並且結束進程終止線程。

這裏是他們使用的例子:

(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) 

那些研究過分叉,並在線程類,常常給的例子。這篇文章的目的是爲了證明通過延續,您可以通過手動保存和恢復其狀態(即延續)在單個線程中實現類似的結果。

P.S. - 我想我在On Lisp中記得類似這樣的東西,所以如果你想看專業代碼,你應該檢查一下這本書。

4

基本上,延續到停止執行並然後再爬起來它在以後的時間點離開的地方的能力的函數。在C#中,您可以使用yield關鍵字來完成此操作。如果你願意的話,我可以詳細介紹一下,但你想要一個簡潔的解釋。 ;-)

2

我仍然得到「拿來主義」,以延續,但一個方法來思考他們,我覺得有用的程序計數器(PC)的概念的抽象。 PC「指向」要在內存中執行的下一條指令,但當然,該指令(以及幾乎每條指令)都會隱式或顯式指向後面的指令,以及任何應該中斷服務的指令。 (即使是NOOP指令也會隱式地執行內存中下一條指令的JUMP,但是如果發生中斷,那麼通常會涉及到內存中某些其他指令的JUMP。)

程序中的每個潛在「活動」點在任何給定點控制可能跳到的記憶中,在某種意義上說是積極的延續。其他可以達到的點是潛在的主動延續,但更重要的是,它們是可能「計算」的延續(可能是動態的),作爲達到一個或多個當前有效延續的結果。

這似乎有點出在傳統的介紹給延續,其中執行的所有未決線程被明確地表示爲延續到靜態代碼位;但是它考慮到了這樣一個事實,即在通用計算機上,PC指向一個指令序列,該指令序列可能會改變表示該指令序列的一部分的存儲器的內容,從而基本上創建一個新的(或修改過的,如果你願意的話) )在飛行中的延續,這種延續並非真正存在於創建/修改之前的延續激活。

所以延續可以被看作是PC的一個高級模型,這就是爲什麼它在概念上包含了普通的過程調用/返回(就像古鐵過程調用/通過低級JUMP,又名GOTO,返回指令加上PC上的電話錄音和恢復時的恢復)以及例外,線程,協同程序等。

所以就像PC指向在「未來」中發生的計算一樣,延續也是一樣的的東西,但在更高,更抽象的層面。個人電腦隱含地指內存加上所有內存位置和寄存器「綁定」到任何值,而後續通過與語言相關的抽象表示未來。

當然,雖然可能存在通常只是一個每計算機(核心處理器)PC,有事實上許多「活性」 PC-ISH實體,如上面所提到的。中斷向量包含一堆,堆棧多一些,某些寄存器可能包含一些等等。當它們的值被加載到硬件PC時,它們「被激活」,但延續是概念的抽象,而不是PC或它們的精確等價(沒有「主」延續的固有概念,儘管我們經常認爲並用這些術語編碼來使事情保持簡單)。

實質上,延續是「被調用時接下來做什麼」的表示,並且因此可以是(並且在一些語言中並且在延續傳遞式程序中通常是)被實例化,傳來傳去,並丟棄就像大多數其他任何數據類型的類對象,就像如何一個典型的計算機將內存位置面對面的人的PC - 作爲幾乎與普通的整數互換。

11

你可能比你認爲的更瞭解他們。

例外是「向上只有」延續的例子。它們允許代碼深入棧中調用異常處理程序來指示問題。

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() 

在這兩種情況下,這些必須被添加到語言明確,而與延續語言,程序員可以創建這些東西,他們是不可用的。

7

思考延續的一種方法是作爲處理器堆棧。當你用「call-with-current-continuation c」調用你的函數「c」時,傳遞給「c」的參數就是你當前的棧,其上有所有的自動變量(表示爲另一個函數,稱之爲「k 「)。同時處理器開始創建一個新的堆棧。當您調用「k」時,它會在原始堆棧上執行「從子程序返回」(RTS)指令,將您跳回原來的「call-with-current-continuation」(從現在起的「call-cc」上),並讓您的程序像以前一樣繼續。如果您將參數傳遞給「k」,則這將成爲「call-cc」的返回值。

從原始堆棧的角度來看,「call-cc」看起來像是一個正常的函數調用。從「c」的角度來看,你的原始堆棧看起來像一個永不返回的函數。

對於一個數學家來說,有一個古老的笑話,他通過爬進籠子裏,抓住一隻籠子裏的獅子並鎖定它,並宣稱自己在籠子外面,而其他所有物體(包括獅子)都在籠子裏面。延續有點像籠子,「c」有點像數學家。你的主程序認爲「c」在裏面,而「c」則認爲你的主程序在「k」裏面。

您可以使用continuation創建任意的控制流結構。例如,你可以創建一個線程庫。 「yield」使用「call-cc」將當前的繼續放在隊列中,然後跳轉到隊列頭上的那個。信號量也有其自己的掛起延續隊列,並且通過將線程從信號量隊列中取出並將其放到主隊列中來重新安排線程。

2

在C#中,您可以訪問兩個繼續。一個通過return訪問,讓一個方法繼續它被調用的地方。另一個通過throw訪問,讓方法繼續在最接近的匹配catch

有些語言讓您將這些語句視爲一等價值,因此您可以將它們分配並在變量中傳遞它們。這意味着你可以隱藏returnthrow的值,並在以後打電話給真的準備返回或拋出。

Continuation callback = return; 
callMeLater(callback); 

這可以在很多情況下得心應手。一個例子就像上面的例子,你想暫停你正在做的工作,並在事情發生的時候(比如獲取Web請求等)恢復它。

我正在使用他們在我正在處理的幾個項目中。其中之一是我正在使用它們,因此我可以在通過網絡等待IO時暫停程序,然後再恢復。另一方面,我正在編寫一種編程語言,讓用戶可以訪問continuations-as-values,這樣他們就可以爲自己編寫returnthrow - 或者任何其他控制流,例如while循環 - 而無需爲此執行他們。

1

想想線程。一個線程可以運行,並且你可以得到它的計算結果。延續是您可以複製的線程,因此您可以運行相同的計算兩次。

1

繼續對Web編程重新產生興趣,因爲它們很好地反映了Web請求的暫停/恢復特性。服務器可以構造代表用戶會話的連續性,並在用戶繼續會話時恢復。

相關問題