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 тип возврата. Как я мог это сделать? Я делаю это правильно?

1 ответ

Решение

Вы, безусловно, на правильном пути! Вот простой пример, который демонстрирует, что вам нужно сделать:

// 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! создает утечки памяти.

  • Вы можете отменить вычисления, используя токен отмены - просто передайте токен при его запуске.

Другие вопросы по тегам