F # quente para parar Async.Start

Sep 04 2020

Olá, tenho uma pergunta sobre async em F #.

Portanto, tenho um procedimento simples que é executado em background que é colocado em um membro de um tipo e se parece com:

type Sender () = 
     member this.Start udpConectionPool = 
        async { (* ome operation that continusly send something to throu udp*) } |> Async.Start

Então, isso começa e começa a enviar quadros continuamente por UDP sem bloquear o resto do programa, mas de vez em quando eu quero reiniciar o thread (digamos que eu queira adicionar um novo ponto de extremidade que o enviaria para o parâmetro udpConectionPool).

Eu estava pensando em algo como despejar uma tarefa para um membro e então:

member this.Stop = async { do! (*stop async start member that contains task*)}

E então posso reiniciar esta tarefa com o pool de conexão atualizado, mas não sei se posso fazer isso. Minha pergunta é: é possível interromper essa tarefa ou, se não, existe uma maneira melhor de fazê-lo?

Respostas

4 TomasPetricek Sep 04 2020 at 20:56

A maneira padrão de cancelar fluxos de trabalho assíncronos F # é usando a CancellationToken. Ao ligar Async.Start, você pode fornecer um token de cancelamento. Quando o token é cancelado, o fluxo de trabalho assíncrono é interrompido (após o término do trabalho de bloqueio atual):

open System.Threading

let cts = new CancellationTokenSource()
let work = async { (* ... *) }
Async.Start(work, cts.Token)

cts.Cancel() // Sometime later from another thread

Para integrar isso com o Sender, você pode armazenar o atual CancellationTokenSourcee ter um Stopmétodo que o cancela (se quiser mantê-lo dentro de uma classe com estado). Como alternativa, você pode retornar IDisposabledo Startmétodo de maneira semelhante à forma como a Observableinterface funciona:

type Sender () = 
  member this.Start udpConectionPool = 
    let cts = new CancellationTokenSource()
    let work = async { (* ... *) }
    Async.Start(work, cts.Token)
    { new System.IDisposable with
      member x.Dispose() = cts.Cancel() }

Dessa forma, o chamador de Starté responsável por armazenar o que foi devolvido IDisposablee descartá-lo antes de ligar Startnovamente.