Il più semplice possibile implementazione mi veniva in mente simile a questa:
open FSharp.Control
let getOneOrOther() =
let queue = BlockingQueueAgent(1)
let async1 = async {
do! Async.Sleep (System.Random().Next(1000, 2000))
do! queue.AsyncAdd(1) } |> Async.Start
let async2 = async {
do! Async.Sleep (System.Random().Next(1000, 2000))
do! queue.AsyncAdd(2) } |> Async.Start
queue.Get()
for i in 1..10 do
printfn "%d" <| getOneOrOther()
Console.ReadLine() |> ignore
Essa si basa sulla realizzazione di coda di blocco dal progetto FSharpx, che probabilmente si vuole per altri motivi. Ma se non si desidera alcuna dipendenza, System.Collections.Concurrent
include anche una coda di blocco, con un'interfaccia leggermente meno piacevole.
Per una versione più generale con cancellazione incorporata, la versione di seguito prende uno Seq<unit -> Async<'T>>
e restituisce il primo risultato a venire attraverso, annullando tutti gli altri.
open FSharp.Control
open System.Threading
let async1() = async {
do! Async.Sleep (System.Random().Next(1000, 2000))
return 1 }
let async2() = async {
do! Async.Sleep (System.Random().Next(1000, 2000))
return 2 }
let getFirst asyncs =
let queue = BlockingQueueAgent(1)
let doWork operation = async {
let! result = operation()
do! queue.AsyncAdd(result) }
let start work =
let cts = new CancellationTokenSource()
Async.Start(work, cts.Token)
cts
let cancellationTokens =
asyncs
|> Seq.map doWork
|> Seq.map start
let result = queue.Get()
cancellationTokens
|> Seq.iter (fun cts -> cts.Cancel(); cts.Dispose())
result
for i in 1..10 do
printfn "%A" <| getFirst [async1;async2]
Console.ReadLine() |> ignore
Ti stai aspettando che le altre attività vengano interrotte o solo per farle continuare in background? – mavnn
Non voglio abortire, ma potrebbe essere in futuro, voglio estendere questo metodo a qualcosa come WaitForAnySuccessfullAndFaiIfAllFail. Sono nuovo in f #. In scala mondiale è stato piuttosto semplice implementarlo con futures e promesse. –