2014-11-03 232 views
7

我有一個需要在一個循環接受客戶端運行的套接字服務器,所以我發現,在funcional編程,遞歸循環使用:在F#F#連續循環

let awaitConnections (wsl:WebSocketListener) = 
    let rec loop()= 
     async { 
      let! ws = Async.AwaitTask (wsl.AcceptWebSocketAsync(cancellation.Token)) 
      printfn "Connection from %s" (ws.RemoteEndpoint.Address.ToString()) 
      Async.Start <| awaitMessages ws 
      do! loop()} 
    loop() 

而且此代碼調用通過這樣做:

Async.Start <| awaitConnections listener 

考慮到應用程序連續運行,我應該使用迭代方法嗎? rec方法是否創建嵌套執行堆棧?

另外,我想循環結束後做一些事情,如:

let awaitConnections (wsl:WebSocketListener) = 
    let rec loop()= 
     async { 
      let! ws = Async.AwaitTask (wsl.AcceptWebSocketAsync(cancellation.Token)) 
      printfn "Connection from %s" (ws.RemoteEndpoint.Address.ToString()) 
      Async.Start <| awaitMessages ws 
      do! loop()} 
    loop() 
    printf "The loop ended" // <-- this line 

但後來它不能編譯,因爲awaitConnections返回類型。我怎麼能這樣做?我做對了嗎?

回答

17

你當然是在正確的軌道上!下面是一個簡單的例子,說明了你需要做什麼:

// Loop that keeps running forever until an exception happens 
let rec loop() = async { 
    do! Async.Sleep(1000) 
    printfn "Working" 
    return! loop() } 

// Call the loop in a try .. finally block to run some cleanup code at the end 
let main() = async { 
    try 
    do! loop() 
    finally 
    printfn "That's it!" } 

// Start the work in the background and create a cancellation token 
let cts = new System.Threading.CancellationTokenSource() 
Async.Start(main(), cts.Token) 
// To cancel it, just call: cts.Cancel() 

幾個重點:

  • 一個無限循環結束後,將無法真正運行的代碼(它是無限的!),但你可以使用try .. finally在塊取消時運行一些代碼

  • 請注意,使用return!進行遞歸循環更好 - 使用do!會造成內存泄漏。

  • 您可以使用取消令牌取消計算 - 只需在啓動時傳遞令牌即可。

+3

謝謝!我在哪裏可以找到更多關於'return!'和'do!'的信息? – vtortola 2014-11-03 02:46:38

+1

這是一個加載的問題,但我會給它一個鏡頭。類似'async'的計算表達式是一系列函數調用的語法糖,返回元素是函數的結果。 'do!'定義了一個返回'unit'的表達式,它在本例中基本上創建了一個新的'Async <'a>'對象,它除了在一次遞歸調用和下一次調用之間進行外,'return!'避免了這種情況。無可否認,這需要思考一下'Async'對象如何在幕後工作以使所有事情都發生。 – 2014-11-03 04:54:52

+0

謝謝,非常有幫助。 – vtortola 2014-11-03 08:21:36